auto和decltype
auto:
auto能让编译器通过初始值来推算变量的类型。显然auto定义的变量必须有初始值:
1 int val1 =1; 2 3 int val2 = 2; 4 5 auto cnt = val1 + val2;//val1+val2的结果是int类型,因此cnt也是int类型
使用auto也能在一条语句中声明多个变量。因为一条语句只能有一个基本类型,所以该语句中所有变量的初始值的变量类型必须一致:
1 auto cnt1 = 1, cnt2 = 1;//正确 2 3 auto x1 = 0, x2 = 3.14;//错误
复合类型,常量和auto:
编译器推断出来的auto类型有时候和初始值的类型并不完全一样,编译器会适当地改变结果类型使其更符合初始化规则。
当引用被当作auto的初始值时,真正参与初始化的其实是引用的对象的值。此时编译器以引用对象的类型作为auto的类型:
int cnt = 1, &x = cnt;
auto y = x;//y是int类型,和x的引用对象cnt一致
auto一般会忽略掉顶层const,保留底层const,比如初始值是一个指向常量的指针时:
1 int cc = 0; 2 3 const int cnt = cc, &x = cnt; 4 5 auto a = cnt;//a是一个int类型(cnt的顶层const被忽略掉了) 6 7 auto b = x;//b是一个int类型,x是cnt的别名,而x是一个顶层const 8 9 auto c = &cc;//c是一个指向int变量的指针(int变量的地址就是指向int类型的指针) 10 11 auto d = &cnt;//d是一个指向int常量的指针(对常量对象取地址是一种底层const) 12 13 //如果希望推断出的auto类型是一个顶层const,需要明确指出 14 15 const auto e = cnt;//cnt推演类型是int,e的类型是const int
1 int cnt = 2; 2 3 int cc = 1; 4 5 const int *x = &cnt; 6 7 auto y = x; 8 9 // *y += 1;//底层const保留了,所以y和x一样是一个指向常量cnt的指针,不能通过改变y来修改cnt的值 10 11 cout << x << " " << y << endl;//x和y的值相同,都是存储的cnt的地址 12 13 int *const p = &cnt; 14 15 auto gg = p;//顶层const被忽略了,即gg是一个指向int的指针 16 17 gg = &cc;//所以gg的值可以修改,即gg可以指向其他对象
还可以将引用的类型设置为auto,此时原来的初始化规则仍然适用:
1 const int cnt = 1; 2 3 auto &a = cnt;//a是一个int常量引用,绑定到cnt 4 5 //auto &b = 1024;//错误,不能非常量引用绑定字面值 6 7 const auto &b = 1024;//可以为常量引用绑定字面值
通过上面第二条语句还可以发现设置一个类型为auto的引用时,初始值中的顶层const仍然保留。但是给初始值绑定一个常量引用则此时顶层常量会被忽略。
decltype:
有时会遇到这种情况:希望从表达式的类型推断出要定义的变量的类型,但是不想用该变量的表达式初始化变量。为了满足这一要求,c++11标准引入了类型说明符decltype,它的作用时选择并返回操作数的数据类型。在此过程中,编译器会分析表达式并得到它的类型,却不实际计算表达式的值:
decltype(f()) sum = x;//sum的类型就是函数 f 的返回类型
编译器并不实际调用函数 f,而是使用当调用发生时 f 的返回值类型作为 sum 的类型。
decltype 处理顶层 const 和引用的方式与 auto 有些许不同。如果 decltype 使用的表达式是一个变量,则 decltype 返回该变量的类型(包括顶层 const 和引用在内):
1 const int x = 0, &cnt = x; 2 3 decltype(x) a = 0;//a的类型是const int 4 5 decltype(cnt) b = a;//b的类型是const int&, 绑定的对象是a 6 7 // decltype(cnt) c;//c是一个引用,必须初始化
值得注意的是引用从来都是作为其所绑定对象的同义词出现,只有在 decltype 处是一个例外。
如果 decltype 使用的表达式不是一个变量,则 decltype 返回表达式结果对应的类型:
1 int cnt = 1024, *p = &cnt, &r = cnt; 2 3 decltype(r + 0) b;//r + 0的结果是一个int类型,因此b是一个为初始化的int变量 4 5 decltype(*p) c;//错误,c是int&类型,必须初始化
通过第三条语句可以发现,如果表达式的内容是解引用操作,则 decltype 将得到引用类型。正如我们所熟悉的那样,解引用指针可以得到指针所指的对象,而且还能给这个对象赋值。因此 decltype(*p) 的结果类型是 int&,而非 int。
还需要特别当心的是,decltype 的结果类型与表达式形式密切相关。对于 decltype 所用的表达式来说,如果变量名加上了一对括号,则得到的类型与不加是有区别的。后者得到的结果就是表达式结果的类型,而前者得到的类型一定是引用类型。因为如果给变量加上了一层或多层括号,编译器就会把它当成是一个表达式。变量是一种可以作为赋值语句左值的特殊表达式,所以这样的 decltype 就会得到引用类型:
1 int cnt = 0, x = 1024; 2 3 decltype(cnt) a;//a是一个int类型变量 4 5 decltype((cnt)) b = x;//b是一个int&类型 6 7 decltype(((cnt))) c = x;//c是一个int&类型