c++11 对’>’的改进
- c++98中如果在模板定义中
Y<X<int>>
这样写会编译失败,>>
之间需要有空格,在C++11中已经改进,不再需要空格。
auto类型
- 要求编译器 根据值对变量进行自动推导。因为是根据值来进行推导,因此必须是立即初始化变量,
auto i;
这种就非法。auto不是类型声明,而是占位符,在编译期间进行推导替换成实际类型。 - auto的好处除了使用简单,还能避免一些隐士转换带来的精度损失。比如返回值是double,但调用方使用的是float来存储,就带来了精度损失,用auto就没有这个问题。
- 在模板编程是带来更多好处。
template<typename T1,typename T2>
sum(T1 & t1,T2 &t2){
auto t3 = t1 + t2;
return t3;
}
- 定义地址时,auto * 和auto 是一样的,如果想定义引用,必须用auto &.
auto * p = &a;auto p = &a; auto & r = a
- C++中,const和volatile一起成为cv限制符(qualifier) 。auto可以和cv一起使用,但是不能改变变量的cv。
doubel foo();
float * bar();
const auto a = foo();//const double
volatile auto &b = bar();//volatile float *
auto d = a;//double
auto &e = a;//const double &
decltype
- 编译时进行类型推导,经常和auto/typedef/using一起使用。
- 使用decltype可以重用匿名类型。
- 可以增大泛型编程的灵活性。
- decltype只接受表达式做参数,不接受函数名做参数
- 推导函数的返回类型是使用基于decltype的result_of模板类
注意
int i;
decltype(i) a;//int a
decltype((i)) b;// 编译失败
编译器在进行类型推导是很复杂的,对于decltype(e),按照以下四个顺序判断
1. 如果e是没有括号的标记表达式(id-expression)或类成员访问表达式,那么decltype(e)就是e命名的实体类型。
2. 否则,假设e类型是T,如果e是一个将亡值(xvalue),则decltype(e)是T&&
3. 否则,假设e类型是T,如果e是一个左值(lvalue),则decltype(e)是T&
4. 否则,假设e类型是T,则decltype(e)是T。
int i=4;
int arr[5]={0};
int*ptr=arr;
struct S{double d;}s;
void Overloaded(int);
void Overloaded(char);//重载的函数
int&&RvalRef();
const bool Func(int);
//规则1:单个标记符表达式以及访问类成员,推导为本类型
decltype(arr)var1;//int[5],标记符表达式
decltype(ptr)var2;//int*,标记符表达式
decltype(s.d)var4;//double,成员访问表达式
decltype(Overloaded)var5;//无法通过编译,是个重载的函数
//规则2:将亡值,推导为类型的右值引用
decltype(RvalRef())var6=1;//int&&
//规则3:左值,推导为类型的引用
decltype(true?i:i)var7=i;//int&,三元运算符,这里返回一个i的左值
decltype((i))var8=i;//int&,带圆括号的左值
decltype(++i)var9=i;//int&,++i返回i的左值
decltype(arr[3])var10=i;//int&[]操作返回左值
decltype(*ptr)var11=i;//int&*操作返回左值
decltype("lval")var12="lval";//const char(&)[9],字符串字面常量为左值
//规则4:以上都不是,推导为本类型
decltype(1)var13;//int,除字符串外字面常量为右值
decltype(i++)var14;//int,i++返回右值
decltype((Func(1)))var15;//const bool,圆括号可以忽略
//编译选项:g++ -std=c++11-c 4-3-10.cpp
- 与auto不同,decltype要带走表达式里的cv限定符,用decltype推导类成员变量时,不会继承类的cv限定符
#include <type_traits>
#include <iostream>
using namespace std;
int i=1;
int&j=i;
int*p=&i;
const int k=1;
int main(){
decltype(i)&var1=i;
decltype(j)&var2=i;//冗余的&,被忽略
cout<<is_lvalue_reference<decltype(var1)>::value<<endl;//1,是左值引用
cout<<is_rvalue_reference<decltype(var2)>::value<<endl;//0,不是右值引用
cout<<is_lvalue_reference<decltype(var2)>::value<<endl;//1,是左值引用
decltype(p)*var3=&i;//无法通过编译
decltype(p)*var3=&p;//var3的类型是int**
auto*v3=p;//v3的类型是int*
v3=&i;
const decltype(k)var4=1;//冗余的const,被忽略
}
//编译选项:g++ -std=c++11 4-3-13.cpp
追踪返回类型
- 追踪返回类型配合auto和decltype可以释放c++的泛型编程能力。
- 在C++98中,如果模板函数返回值依赖于参数类型,则返回值在模板实例化前都没法确定,我们在定义函数模板就会遇到麻烦。比如模板Sum(T1 t,T2 t).
最直观的写法
template<typename T1,typename T2>
decltype(t1+t2) Sum(T1 t1,T2 t2){
return t1+t2;
}
这种写法有问题,编译器在推导decltype(t1+t2)时,表达式t1和t2都没有声明(编译器只会从左往右读符号),c++编译器规则变量使用前必须声明,因此c++11引入新语法,追踪返回类型,来声明这类函数。
template<typename T1,typename T2>
auto Sum(T1 t1,T2 t2)->decltype(t1+t2){
return t1+t2;
}
- 将返回值写在参数声明之后,让编译器来推导函数模板返回类型。
基于范围的for循环
对于stl里的有明确范围的集合,可以使用for加冒号直接遍历循环
vector<int> v = {1,2,3,4,5};
for(auto i:v){
cout<<i;
}
- 注意使用范围循环且使用auto的话,变量表示的是解引后的对象,不是迭代器。更改变量值不会改变集合里的值,想用引用的方式得用auto &。