推导规则
前面我们介绍过auto的类型推导规则。找到c++中,decltype也可以获取变量或表达式的类型,那么它的类型推导和auto一样吗,应该如何使用呢?
decltype与auto的推导规则并不同。例如:
int t;
const int& x= t;
auto y = x; // y的类型 int
decltype(x) z; // z的类型 const int&
decltype(x + y) u; // u的类型 int
前面讲过,auto在推导过程中,会去掉变量本身的const和引用。而decltype则会完整地保留。
但需要注意的是:如果decltype不是直接应用在一个变量上,而是应用在一个表达式,且该表达式返回的是一个左值,那么decltype会得到一个左值引用。
具体是什么情况呢?
std::vector<int> vec = {1,2,3};
int x = 0;
int& GetItem(int i) {return x;}
decltype(vec[i]) a; // int&
decltype(GetItem(1)) b; // int&
decltype(++x) c; // int&
decltype((x)) d; // int&
本例中的4种情况均属于左值表达式的情况。其中最后一种需要注意,仅仅是在x外面加了一个括号,从变量x变成了表达式(x),decltype推导的类型也就从int变成了int&。
应用场景
decltype一个常见的应用场景是,用返回值后置语法来声明模板函数。例如,我们想定义一个函数来代替容器的operator[]操作:
template<typename Container, typename Index>
auto Access(Container &c, Index i)
-> decltype(c[i]) {
return c[i];
}
在本例中,函数的返回值类型需要根据参数类型来确定,而正常的函数声明,返回值类型写在参数声明之前,所以没办法依赖参数。而使用返回值类型后置语法就可以避免这个问题。
C++14新特性
在c++14中,函数的返回值类型可以根据return语句自动推导,也就是对于上面的例子,省略掉尾部的decltype类型声明,只保留前面的auto,也可以编译通过。
但是,这样一来,函数返回值类型会按照模板的类型推导规则进行推导,也就是会去掉const和引用。这与decltype的推导规则并不相同。那么,如何能按照decltype的推导规则来定义返回值呢?其实可以将decltype与auto结合使用:
template<typename Container, typename Index>
decltype(auto) Access(Container &c, Index i) {
return c[i];
}
decltype(auto)不仅能在函数返回值中使用,在变量声明的场景也可以使用:
int a;
const int& x = a;
auto y = x; // y类型 int
decltype(auto) z = x; // z类型 const int&