关于decltype关键字
decltype是C++0x所引入的用于提取表达式类型的新关键字,其语法形式为:decltype(expression) 。这种语法形式在C++0x草案中被称为decltype类型指示符。
根据C++0x最终草案,decltype(e)所提取的类型由以下规则决定:
- 如果e是一个没有外层括弧的标识符表达式或者类成员访问表达式,那么decltype(e)就是e所命名的实体的类型。
- 如果e是一个函数调用或者一个重载操作符调用(忽略e的外层括弧),那么decltype(e)就是该函数的返回类型。
- 否则,假设e的类型是T:若e是一个左值,则decltype(e)就是T&;若e是一个右值,则decltype(e)就是T。
比如,
int a;
int f(void);
decltype(a) x1 = a; //相当于 int x1 = a;(规则1,e是标识符)
decltype(f()) x2 = a; //相当于 int x2 = a;(规则2,e是函数调用)
decltype((a)) x3 = a; //相当于 int& x3 = a;(规则3,e是左值)
decltype(a+1) x4 = a; //相当于 int x4 = a;(规则3,e是右值)
注意:decltype(e)中的操作数e仅用于推导类型,编译器不会计算e的值。
关于“返回类型后置语法”
返回类型后置语法是C++0x所引入的新型函数语法。与传统的C系列函数语法不同,在这一语法中,函数返回类型处于函数声明的末端而不是前端(类似于BASIC或PASCAL)。
其语法形式为:auto 函数名(函数签名) -> 返回类型
比如,若使用返回类型后置语法,以下函数声明
void f(int);
就将被改写为:
auto f(int) -> void;
C++0x中的lambda表达式也将采用类似的返回类型后置语法,比如:
[](int) -> void{};
在后置返回类型(trailing-return-type)这一点上,普通函数与lambda表达式的区别在于:
- 前导符不同,lambda表达式使用[] 符号,而普通函数则使用auto关键字。
注:为达成一致性,曾经有提案建议普通函数也应使用[] 符号作为前导符。(出于美学考虑,该提案未被标准委员会采纳)
- lambda表达式的后置返回类型在某些情况下可省略,而普通函数的后置返回类型在任何情况下都不能省略。
注:为达成一致性,曾经有提案建议普通函数的后置返回类型在同等情况下也应可省略。(出于复杂性考虑,该提案未被标准委员会采纳)
联合使用decltype类型指示符与返回类型后置语法
在实现泛型算法的函数模板中,decltype类型指示符与返回类型后置语法常被搭配使用,以达到使用函数的形参自动推导返回值类型的目的。比如: #include <iostream> using namespace std; //C++03 version template<class R, class T, class U> R mul1(T x, U y) { return x*y; } //C++0x version template<class T, class U> auto mul2(T x, U y) -> decltype(x*y) { return x*y; } int main() { cout << mul1<double>(1, 3.0) << endl; cout << mul2(1, 3.0) << endl; return 0; }
试比较上述示例代码所实现的两个泛型乘法函数mul1(C++03版本)和mul2(C++0x版本):
- 在C++03版本中,由于缺乏相应的语言设施,mul1函数无从推导返回值的类型,被迫引入了返回值类型R作为模板参数。客户端调用mul1时必须提供返回值类型。
- 而在C++0x版本中,通过结合使用decltype类型指示符与返回类型后置语法,mul2函数成功地推导出了返回值的类型。客户端调用mul2时也就无需再提供返回值类型。
这里使用返回类型后置语法的理由是:用于推导返回类型的形参x与y在函数签名中才得以声明,在auto关键字所处的位置上因尚未进入作用域而无法使用。
补记
只用decltype而不用返回类型后置语法的解决方案: template<class T, class U> decltype(*(T*)0**(U*)0) mul3(T x, U y) { return x*y; } template<class T, class U> decltype(std::declval<T>()*std::declval<U>()) mul4(T x, U y) { return x*y; }