Understand decltype
给定名称或表达式, decltype
就会告诉你这个名字或者表达式的类型。decltype
只是简单的返回名字或者表达式的类型,很正常。
1 | const int i = 0; //decltype(i)是const int |
在C++11中,auto
不能被用作函数模板的返回类型。因此decltype
最主要的用途就是用于声明函数模板,而这个函数返回类型依赖于形参类型。
[!NOTE]
在 C++14 中,
auto
不能用作函数的返回类型,除非使用的是 C++14 中的泛型 lambda 表达式。此外,使用auto
作为函数返回类型还有一些限制,主要涉及到函数模板和非静态成员函数的情况。
函数模板中的auto返回类型:在函数模板中,如果使用
auto
作为返回类型,编译器无法推导出实际类型,因为模板的参数类型可能在调用时才能确定。因此,在函数模板中使用auto
作为返回类型是不合法的。
1
2
3
4
5 // 不合法的例子
template<typename T>
auto foo(T x) {
return x;
}非静态成员函数中的auto返回类型:C++ 中,非静态成员函数必须与类的实例相关联才能被调用。因此,在类定义时,非静态成员函数的声明只是描述了函数的接口,而不涉及任何特定实例的情况。返回类型的推导需要考虑类的实例。这些信息直到使用对象调用该函数时才会完全可见,因此在函数声明处无法推导出完整的返回类型,非静态成员函数不能使用
auto
作为返回类型。
1
2
3
4
5
6
7 // 不合法的例子
class MyClass {
public:
auto myFunc() {
return 42;
}
};
C++11 中的第一个版本是:
1 | template<typename Container, typename Index> //可以工作, |
函数名称前面的auto
不会做任何的类型推导工作。相反的,他使用了C++11的尾置返回类型语法,它允许使用入参作为返回类型推导的一部分。
C++11允许自动推导单一语句的lambda表达式的返回类型, C++14扩展到允许自动推导所有的lambda表达式和函数。因此在 C++14 中, 我们可以省略尾置返回类型而只使用 auto。先贡献一个错误版本:
1 | template<typename Container, typename Index> //C++14版本, |
在这里, d[5] 理应返回一个 int&, 但是auto类型推导(此处实际是模板类型推导的那套规则)去除了了引用, 因此推导出了 int 的返回类型,是个右值。
所以我们需要使用decltype
类型推导来推导它的返回值。decltype(auto) 的规则: auto 指定推导类型, 但推导时应该使用decltype 规则。
1 | template<typename Container, typename Index> //C++14版本,可以工作,但是还需要改良 |
decltype(auto)
的使用不仅仅局限于函数返回类型,也可以对初始化表达式使用decltype
推导的规则:
1 | Widget w; |
目前容器是通过左值引入一个非const传递的, 如果也可以传递右值就好了。我们可以为右值容器定义一个重载版本, 但维护起来太麻烦了。这时我们需要通用引用。
1 | template<typename Container, typename Index> //最终的C++14版本 |
decltype
有一些特殊情况:对于单纯的变量名,decltype
只会返回变量的声明类型。然而,对于比单纯的变量名更复杂的左值表达式,decltype
报告的类型始终是左值引用。
1 | decltype(auto) f1() |
重点
- decltype 总是不加修改的产生变量或者表达式的类型。
- 对于名称以外的类型 T 的左值表达式, decltype 总是返回类型 T&.
- C++14 支持 decltype(auto), 它与 auto 一样, 从其初始值设定项中推导类型, 但它使用 decltype 规则执行类型推导。