C++中decltype的推导规则详解
我们先来看看 decltype(exp)的推导规则:
口推导规则 1,exp是标识符、类访问表达式,decltype(exp)和exp的类型一致。
口推导规则 2,exp是函数调用,decltype(exp)和返回值的类型一致。
口推导规则 3,其他情况,若exp是一个左值,则decltype(exp)是exp类型的左值引用,否则和 exp 类型一致。
只看上面的推导规则,很难理解 decltype(exp)到底是一个什么类型。为了更好地讲解这些规则的适用场景,下面根据上面的规则分3种情况依次讨论
1)标识符表达式和类访问表达式。
2)函数调用(非标识符表达式,也非类访问表达式)。
3)带括号的表达式和加法运算表达式(其他情况)。
(1) 标识符表达式和类访问表达式
代码清单1-3是一组简单的例子。
class Foo
{
public :
static const int Number = 0;
int x;
};
int n = 0;
volatile const int & x = n;
decltype(n) a = n; // a -> int
decltype(x) b = n; // b -> const volatile int &
decltype(Foo::Number) c = 0; //c -> const int
Foo foo;
decltype(foo.x) d = 0; //d->int,类访问表达式
变量 a、b、c保留了表达式的所有属性(cv、引用)。这里的结果是很简单的,按照推导规则1,对于标识符表达式而言,decltype 的推导结果就和这个变量的类型定义一致。d是一个类访问表达式,因此也符合推导规则1。
(2)函数调用
如果表达式是一个函数调用(不符合推导规则1),结果会如何呢?
请看代码清单1-4所示的示例。
int& func_int_r(void); //左值(lvalue,可简单理解为可寻址值)
int&& func_int_rr(void); //x值(xvalue,右值引用本身是一个xvalue)
int func_int(void); // 纯右值(prvalue)
const int& func_cint_r(void); //左值
const int&& func_cint_rr(void); //x值
const int func_cint(void); //纯右值
const Foo func_cfoo(void); //纯右值
//下面是测试语句
int x = 0;
decltype(func_int_r()) al = x; // al -> int &
decltype(func_int_rr()) b1 = 0; // b1 -> int &&
decltype(func_int()) c1 = 0; // c1 -> int
decltype(func_cint_r()) a2 = x; // a2 -> const int &
decltype(func_cint_rr()) b2 = 0; // b2 -> const int &&
decltype(func_cint()) c2 = 0; // c2 -> int
decltype(func_cfoo()) ff = Foo(); // ff -> const Foo
可以看到,按照推导规则2,decltype的结果和函数的返回值类型保持一致。这里需要注意的是,c2是int而不是constint。这是因为函数返回的int是一个纯右值(prvalue)。对于纯右值而言,只有类类型可以携带cv限定符,此外则一般忽略掉 cy限定。decltype 推导出来的c2是一个int。作为对比,可以看到decltype根据funccfoo()推导出来的f的类型是 const Foo。参见【0voice C++】
(3)带括号的表达式和加法运算表达式
最后,来看看第三种情况:
struct Foo { int x; };
const Foo foo = Foo();
decltype(foo.x) a = 0; //a -> int
decltype((foo.x)) b = a; //b->const int &
int n = 0, m = 0;
decltype(n + m) c = 0; //c -> int
decltype(n += m) d = c; //d -> int &
a和b的结果:仅仅多加了一对括号,它们得到的类型却是不同的。
a的结果是很直接的,根据推导规则1,a的类型就是foo.x的定义类型。
b的结果并不适用于推导规则1和2。根据fo0.x是一个左值,可知括号表达式也是一个左值。因此可以按照推导规则3,知道 decltype的结果将是一个左值引用。