C++ auto和decltype的区别
#include <iostream> using namespace std; int main() { int n = 10; int &r1 = n; //auto推导 auto r2 = r1; r2 = 20; cout << n << ", " << r1 << ", " << r2 << endl; //decltype推导 decltype(r1) r3 = n; r3 = 99; cout << n << ", " << r1 << ", " << r3 << endl; return 0; }
root@ubuntu:~/c++# g++ -std=c++11 auto.cpp -o auto root@ubuntu:~/c++# ./auto 10, 10, 20 99, 99, 99
decltype
decltype是C++11新增的关键字,主要用于提取变量和表达式的类型。
decltype的语法形式为:decltype(e),这里e是一个表达式,而decltype(e)是一个类型指示符。decltype的结果不是值,而是一个类型。
decltype的语法规则主要有以下四条:
如果e是一个没有用小括号括起来的标识符表达式或类成员存取表达式,那么decltype(e)的结果类型为该表达式中标识符的声明类型。
注:这里的小括号是指表达式e自身带的小括号,而不是decltype(e)中的小括号。
如果e是T类型的x值,那么decltype(e)的结果类型为T&&。
注:x值(xvalue)是C++11新引入的值的种类,介于传统的左值和右值之间。最常见的x值为无名右值引用。
如果e是T类型的左值,那么decltype(e)的结果类型为T&。
注:同时满足规则1和规则3的情况下,规则1优先。
如果e是T类型的纯右值,那么decltype(e)的结果类型为T。
注:纯右值(prvalue)即传统右值。字面量以及临时对象都是纯右值。
测试代码及出错信息
#include <iostream> template<typename T> T f(); struct S {int a;}; int main() { int a = 0; S s; f<decltype(a)>(); f<decltype(s.a)>(); f<decltype(std::move(a))>(); f<decltype((a))>(); f<decltype((s.a))>(); f<decltype(0)>(); decltype(a) b = a; // int b = a; decltype((a)) c = a; // int& c = a; }
root@ubuntu:~/c++# g++ -std=c++14 auto.cpp -o auto /tmp/ccRONFza.o: In function `main': auto.cpp:(.text+0x20): undefined reference to `int f<int>()' auto.cpp:(.text+0x24): undefined reference to `int f<int>()' auto.cpp:(.text+0x28): undefined reference to `int&& f<int&&>()' auto.cpp:(.text+0x2c): undefined reference to `int& f<int&>()' auto.cpp:(.text+0x30): undefined reference to `int& f<int&>()' auto.cpp:(.text+0x34): undefined reference to `int f<int>()' collect2: error: ld returned 1 exit status root@ubuntu:~/c++#
这段代码使用了一种特殊技巧来查看decltype的结果类型。代码中定义了一个未被实现的函数模板f,该函数模板有一个类型参数T。如果用某个类型T来调用该函数模板,编译器就会因找不到函数模板的定义而输出”T f<T>()未被定义“的出错信息。也就是说,利用该函数模板,我们便可以在编译器所输出的出错信息中检查模板参数T的实际类型。在main函数内,我们充分利用了这一个技巧,通过利用decltype的结果类型调用该函数模板来让编译器输出相应的类型。
从出错信息中可以得知:
decltype(a)和decltype(s.a)的结果类型为int。(适用规则1)
理由:a是未带外围小括号的标识符,s.a是未带外围小括号的类成员存取表达式。变量a的声明类型是int,S结构的成员变量a的声明类型也是int。
decltype(std::move(a))的结果类型为int&&。(适用规则2)
理由:std::move(a)是无名右值引用,是x值的一种。
decltype((a))和decltype((s.a))的结果类型为int&。(适用规则3)
理由:(a)和(s.a)都是带外围小括号的左值引用,是左值。
decltype(0)的结果类型为int。(适用规则4)
理由:0是纯右值(即传统右值)。
decltype(auto)
decltype(auto)是C++14新增的类型指示符,可以用来声明变量以及指示函数返回类型。
当decltype(auto)被用于声明变量时,该变量必须立即初始化。假设该变量的初始化表达式为e,那么该变量的类型将被推导为decltype(e)。也就是说在推导变量类型时,先用初始化表达式替换decltype(auto)当中的auto,然后再根据decltype的语法规则来确定变量的类型。
decltype(auto)也可以被用于指示函数的返回值类型。假设函数返回表达式e,那么该函数的返回值类型将被推导为decltype(e)。也就是说在推导函数返回值类型时,先用返回值表达式替换decltype(auto)当中的auto,然后再根据decltype的语法规则来确定函数返回值的类型。
#include <iostream> template<typename T> T f(); struct S {int a;}; int a = 0; S s; decltype(auto) g1() {return s.a;} decltype(auto) g2() {return std::move(a);} decltype(auto) g3() {return (a);} decltype(auto) g4() {return (0);} int main() { decltype(auto) i1 = a; decltype(auto) i2 = std::move(a); decltype(auto) i3 = (s.a); decltype(auto) i4 = (0); f<decltype(i1)>(); f<decltype(i2)>(); f<decltype(i3)>(); f<decltype(i4)>();
f<decltype(g1())>(); f<decltype(g2())>(); f<decltype(g3())>(); f<decltype(g4())>(); }
"auto.cpp" 30L, 572C written root@ubuntu:~/c++# g++ -std=c++14 auto.cpp -o auto /tmp/ccSvkNQi.o: In function `main': auto.cpp:(.text+0x78): undefined reference to `int f<int>()' auto.cpp:(.text+0x7c): undefined reference to `int&& f<int&&>()' auto.cpp:(.text+0x80): undefined reference to `int& f<int&>()' auto.cpp:(.text+0x84): undefined reference to `int f<int>()'
auto.cpp:(.text+0x88): undefined reference to `int f<int>()' auto.cpp:(.text+0x8c): undefined reference to `int&& f<int&&>()' auto.cpp:(.text+0x90): undefined reference to `int& f<int&>()' auto.cpp:(.text+0x94): undefined reference to `int f<int>()' collect2: error: ld returned 1 exit status root@ubuntu:~/c++#