auto
C++11之前,最无用的关键字
我们知道,在C++11以前,auto关键字的作用是声明函数内的局部变量为自动的变量。也就是说,它的作用是指出当前的变量为局部变量。是不是很多余?在函数里面声明整型局部变量,直接用 int i 就可以了,几乎没有人写成“规范”的 auto int i。所以auto成为了C++11之前最无用的关键字。
但是C++11标准来了,auto以前的作法全部作废。auto在新标准中被赋予新义,被用于自动类型推断。使用它,让编译器通过初始值来替我们去推断变量的类型。显然,auto定义的变量必须赋有初始值。比如:
auto i = 0, *p = &i; //正确:i是整数,p是整形指针
auto sz = 0, pi = 3.14; //错误:sz和pi的类型不一致
首先需要明确一点的是,对于引用类型,auto看到的是引用的对象,也就是说,在参与auto判断类型的是引用对象的值,而不是这个引用。但是对于指针,和引用是不一样的,auto看到的仍然是这个指针。其次,auto一般会 忽略顶层const,而 保留底层const。
举个例子就清楚了:
int i = 0;
const int ci = i;
const int &cr = ci;
const int* icptr = &ci;
auto b = ci; //auto是int,即 b是一个整数(ci的顶层const特性被忽略掉了)
auto c = cr; //auto是int (虽然cr是底层const,但是auto看的是它实际引用的值,即ci,而ci只是顶层const)
auto c1 = icptr; //auto是const int *
auto d = &i; //auto是int*,d是一个整形指针,指向i
auto e = &ci; //audo等价于const int*(对于常量对象取地址,得到的也是一种底层const)
所以说,如果希望auto推断出的类型具有顶层const属性,则需要手动显示指出:
int i = 0;
const auto f = &i; //f的类型为 int * const
f++; //error: increment of read-only variable 'f'|
对于auto手动显式指定的引用类型,如下例所示:
const int ci = 0;
auto &g = ci; //auto为const int ,g具有底层const属性
auto &h = 42; //error:不能为非常量引用绑定字面值
auto const &j = 42; //正确:可以为常量引用绑定字面值
对于auto,还有两点要说明:
- auto 不能做为模板参数。
//1st
vector<auto>* autoVector = new vector<int>;
//2nd
auto autoVector = new vector<int>;
显然,第二个才能体现 auto 的意义和存在价值。
- auto 不能做为函数的参数类型和返回类型。
decltype
decltype就是 declared type 的缩写。如果希望表达式的类型去推导出要定义的变量的类型,但是不想用该表达式去初始化变量,那么就要用到C++11中新引入的关键字decltype了。它的作用是选择并返回操作数的数据类型。如下所示:
decltype(func()) sum = x; //sum的类型就是函数func()的返回类型
编译器并不会去调用函数func(),而只是根据func的原型去得到其返回类型。
decltype处理顶层const和引用的方式与auto有些不同。如果decltype使用的表达式是一个变量,则decltype返回该变量的类型,包括顶层const和引用在内。而auto对于引用,看到的是引用指向的对象,同时忽略顶层const。
const int ci = 0,&cj = ci;
decltype(ci) x = 0; //x的类型是const int
decltype(cj) y = x; //y的类型是const int&
decltype(cj) z; //error:z是一个引用,但未初始化
再次强调一下,注意区别与auto的不同,对于auto来说,引用一直是作为其所指对象的同义词出现的,只有用在decltype处是个例外。
int i = 10;
const int& iref = i;
decltype(iref) i2ref = i;
i2ref++; //error: increment of read-only reference 'i2ref'
auto i3ref = iref;
i3ref++; //ok
cout << "i = " << i << endl;
cout << "i3ref = " << i3ref << endl;
输出结果为:
i = 10
i3ref = 11
可以看到,i2ref的类型是const int& ,而i3ref的类型是int .
如果decltype使用的表达式不是一个变量,则decltype返回表达式结果对应的类型。如果该表达式是一个左值,则返回引用类型。表达式如果是一个右值,则返回普通类型。
注意,一个变量如果加上一个括号,就变成一个表达式了。
注意区分 ++x 与 x++。前者是左值表达式,后者是右值表达式。前者修改自身值,并返回自身;后者先创建一个临时对像,并用 x 的值赋之,后将修改 x 的值,最后返回临时对像。
左值和右值
说到左值与右值,这里需要区分一下:在C语言里面,左值可以位于赋值语句的左侧,右值则不能。
但是在C++语言中,二者的区别就没那么简单了。
- 首先,变量是左值。
- 赋值运算符得到的结果仍然是一个左值。
- 取地址运算符作用于一个左值运算对象,返回一个指向该运算对象的指针,这是一个右值。
- 内置解引用运算符、下标运算符、迭代器解引用运算符、string和vector的下标运算符的求值结果都是左值。
- 内置类型和迭代器的递增递减运算符作用于左值运算对象,其前置版本所得结果为左值,其后置版本所得结果为右值。
前导函数 ( Forwarding Function)
template<typename T1,class T2>
?type? gt(T1 x, T2 y){
//some code
return x+y;
}
对于上述模板函数的返回类型,我们似乎可以设置为decltype(x+y),但是不幸的是,在函数开头处,还未声明x和y,它们不在作用域内。
auto func(int x, float y) -> double
也可以写为:
auto func(int x, float y) -> decltype(x+y)
其中,-> double被称为后置返回odga(trailing return type)。
现在,对于刚才那个模板函数碰到的问题,有了解决方案:
template<typename T1,class T2>
auto gt(T1 x, T2 y) -> decltype(x+y){
//some code
return x+y;
}