【C++ 11】中的auto关键字可以通过变量的初始化值来推断确定变量的类型。decltype关键字【C++ 11】源于英文declare type(声明类型),它可以推断一个表达式的结果类型。decltype操作符的用法请见下述代码及其注释。

1
2
3
4
5
6
7
int a = 1;
float b = 1.1F;

decltype(a+b) c; //int + float结果为float, c被推断为float
decltype(b*a) d; //float * int结果为float, d被推断为float
float& e = b;
decltype(e) f = b; //e的类型为float&,f被推断为float&

其中,第7行的f被推断为float&,编译器会要求程序员对引用f进行初始化,即它必须与一个float对象相关联。

为了讨论decltype的应用场景,请读者阅读下述代码:

1
2
3
4
5
template <typename T1, typename T2>
decltype(a+b) add(T1& a, T2& b){ //错误:a,b的声明在decltype(a+b)之后
auto c = a + b;
return c;
}

模板函数add( )试图将T1类型的a和T2类型的b相加,其结果类型既可能是T1,也可能是T2,甚至可能是其它类型▲,因此,该模板函数试图通过decltype(a+b)来推断确定返回类型。看起来很合理,但对编译器而言,decltype(a+b)出现时,a,b尚未声明,不符合语法要求。为了解决这个问题,【C++ 11】引入了返回类型后置语法:

1
2
3
4
5
6
template <typename T1, typename T2>
auto add(T1& a, T2& b) -> decltype(a+b) //返回类型后置
{
auto c = a + b;
return c;
}

第2行的auto起到占位的作用,箭头 -> 后面的decltype(a+b)则后置定义了函数的返回值类型。

有点麻烦,对吗? 【C++ 17】对持此相同观点,它去除了这个麻烦。下述代码在C++ 17的编译器下可以正常工作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
//Project - DeclareType
#include <iostream>
using namespace std;

template <typename T1, typename T2>
auto add(T1& a, T2& b) // -> decltype(a+b)在C++ 17中不再被需要
{
auto c = a + b;
return c;
}

int main() {
int a = 1;
float b = 1.1F;

auto c = add(a,b);
auto d = add<float,int>(b,a);
cout << "c = " << c << ", type = " << typeid(c).name() << endl;
cout << "d = " << d << ", type = " << typeid(d).name() << endl;

return 0;
}

上述代码的执行结果为:

1
2
c = 2.1, type = f
d = 2.1, type = f

从执行结果可见,不论是int a + float b还是float b + int a,返回的结果都是float型,auto关键字正确推断了模板函数的返回值类型。