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;
}
main.cpp:(.text.startup+0x5): undefined reference to `int f<int>()'
main.cpp:(.text.startup+0xa): undefined reference to `int f<int>()'
main.cpp:(.text.startup+0xf): undefined reference to `int&& f<int&&>()'
main.cpp:(.text.startup+0x14): undefined reference to `int& f<int&>()'
main.cpp:(.text.startup+0x19): undefined reference to `int& f<int&>()'
main.cpp:(.text.startup+0x1e): undefined reference to `int f<int>()'
这段代码使用了一种特殊技巧来查看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())>();
}
main.cpp:(.text.startup+0x5): undefined reference to `int f<int>()'
main.cpp:(.text.startup+0xa): undefined reference to `int&& f<int&&>()'
main.cpp:(.text.startup+0xf): undefined reference to `int& f<int&>()'
main.cpp:(.text.startup+0x14): undefined reference to `int f<int>()'
main.cpp:(.text.startup+0x19): undefined reference to `int f<int>()'
main.cpp:(.text.startup+0x1e): undefined reference to `int&& f<int&&>()'
main.cpp:(.text.startup+0x23): undefined reference to `int& f<int&>()'
main.cpp:(.text.startup+0x28): undefined reference to `int f<int>()'
这段代码使用了同样的编程技巧来查看decltype(auto)的结果类型。
从出错信息中可以得知:
- 变量i1的类型等同于decltype(a)的结果类型,即int。(适用规则1)
函数g1的返回值类型等同于decltype(s.a)的结果类型,即int。(适用规则1) - 变量i2的类型等同于decltype(std::move(a))的结果类型,即int&&。(适用规则2)
函数g2的返回值类型等同于decltype(std::move(a))的结果类型,即int&&。(适用规则2) - 变量i3的类型等同于decltype((s.a))的结果类型,即int&。(适用规则3)
函数g3的返回值类型等同于decltype((a))的结果类型,即int&。(适用规则3) - 变量i4的类型等同于decltype((0))的结果类型,即int。(适用规则4)
函数g4的返回值类型等同于decltype((0))的结果类型,即int。(适用规则4)