为什么要有 decltype
在使用模版编程时,由于模版传入的参数的类型不确定,通过这些参数计算出来结果的类型也不确定。如下示例:
template<class T1, class T2>
void ft(T1 x, T2 y){
?type? xpy = x + y;
}
这里的 xpy 无法直接定义是什么类型。在原先的 C++98 对这种现象无能为力。因此在 C++11 中,增加了类型定义的方式,就是 decltype。
decltype 定义方式
decltype(x + y) xpy;
这个就是使用 decltype 定义 xpy 的方式。
PS:这里可以理解为用 decltype(x + y) 替换了 int / short / char /...。
编译器确定 decltype 类型的方法
为了表述方便,这里我们可以把 decltype 的定义抽象成:
decltype(expression) var
我们编写了 decltype 之后,编译器在编译过程中就需要确定 decltype(expression) 的具体类型应该是什么。这个时候编译器会按如下优先级来确定 decltype(expression) 中的类型具体应该是什么。一旦匹配到了,后面就不会再去匹配了。
优先级1:expression是没有用括号括起的标识符——完全复刻类型
如果expression是一个没有用括号括起的标识符,则var的类型与该标识符的类型相同,包括const等限定符,下面是几个例子:
double x = 5.5;
double y = 7.9;
double& rx = x;
const double* pd;
decltype(x) w; // w is type double
decltype(rx) u = y; // u is type double&
decltype(pd) v;// v is type const double*
也就是说,在这种情况下, decltype(expression) 的类型完全复刻 expression 的类型。
优先级2:expression 是一个函数——取返回值
如果expression是一个函数调用,则var的类型与函数的返回类型相同:
long indeed(int);
decltype (indeed(3)) m; // m is type in
这个时候是通过函数的原型来查看返回值是什么,而不会真的去执行一遍函数。
PS:如果 decltype 声明的是函数本身应该怎么办?应该直接使用 decltype(indeed) 来表示。但是函数的定义又非常特殊,实际调试猪呢个,不能直接写成:
decltype (indeed) func;
虽然这样定义是成功的,但是没有办法使用(在编译器链接时报错)。实际使用时有两种定义方法
decltype (indeed)& func = indeed;
// 或者
decltype (indeed)* func = indeed;
// 或者
decltype ((indeed)) func = indeed; // 匹配优先级3中的规则
优先级3:expression是一个左值
如果expression是一个左值,则var为指向其类型的引用。
虽然前面例子里面类似 decltype(x) 中的 x 也是左值,但是由于已经符合了优先级1 所以就不会到优先级3 来匹配了。
这里会用另一种方法来表示:
decltype((x)) w;
优先级4:其他情况
如果上述都不满足,则var的类型与expression的类型相同
int j = 3;
int &k = j
int &n = j;
decltype(j +6) i1; // i1 type int
decltype(100L) i2; // i2 type long
decltype(k+n) i3; // i3 type int
使用 decltype 解决函数返回值的不确定问题
前面讲述的都是在函数内部,由于传入参数先做了声明,因此编译器可以通过 decltype 来找到类型。例如:
template<class T1, class T2>
void ft(T1 x, T2 y){
decltype(x + y) xpy = x + y;
}
但是如果返回值也不确定,就不能直接用 decltype 了,下面的代码就是错误的:
template<class T1, class T2>
decltype(x + y) ft(T1 x, T2 y){
return x + y;
}
这是因为在 decltype(x + y) 时还没有声明 x y 的类型,decltype(x + y) 不在作用域内,所以编译器不知道 decltype(x + y) 的类型是什么。
因此 C++11 提供了声明和定义函数的语法,类似于:
auto func(x + y) -> double
前面的 auto 是占位符,后面的 double 才是返回的类型。用这种方式来声明模版的不确定返回值,就是:
template<class T1, class T2>
auto ft(T1 x, T2 y) -> decltype(x + y) {
return x + y;
}
此时的 decltype(x + y) 在 x y 的作用域内,因此是有效的。