《Effective Modern C++》条款3
条款3:理解decltype(Understand decltype)
decltype可以告诉你一个变量或一个表达式的类型。
一般情况下,decltype返回的类型和你所给变量或表达式的类型一模一样
1 | const int i = 0; // decltype(i) is const int |
在C++11中,decltype主要被用在那些其函数返回值依赖于其参数类型的函数模板声明中。
1 | template<typename Container, typename Index> |
在C++14中,所有的lambda和函数的返回类型都可以被推导(C++11只支持单语句的lambda的返回类型推导)。因此,在C++14中,上例可以被写为:
1 | template<typename Container, typename Index> |
在条款2中说到,对函数返回值中的auto,编译器采用模板类型推导。对于大多数的容器来说,c[i]的类型为T&,但根据条款1中所述,在模板类型推导过程中,初始化表达式的常量性(&)被忽略,则本例中authAndAccess函数的返回值就为int,而不是所期望的int&。
为了使得authAndAccess函数所我们期望的一样,在C++14中,我们需要使用decltype类型推导来推导出其返回值类型。1
2
3
4
5
6
7template<typename Container, typename Index>
decltype(auto)
authAndAccess(Container& c, Index i)
{
authenticateUser();
return c[i];
}
decltype(auto)含义:
auto指示类型需要被推导,decltype指示在类型推导过程中使用decltype的规则。
当你在初始化表达式中想要使用decltype推导规则时,可以使用decltype(auto)来声明变量。
1 | Widget w; |
再来看一下,authAndAccess函数在C++14中的声明:
1 | template<typename Container, typename Index> |
该函数只接受左值,而不接受右值(因为,右值不能绑定到一个非常量左值引用:Container&)。为了让该函数既接受左值,又接受右值,一个方法是重载该函数(一个重载版本声明为左值引用参数,另一个重载版本声明为右值引用参数),但是这带来的缺点是:需要维护两个函数。另一种避免出现这种情况的方式是:使用万能引用,见条款24。
1 | template<typename Container, typename Index> |
然而,根据条款25,需要在万能引用上使用std::forward:1
2
3
4
5
6
7template<typename Container, typename Index>
decltype(auto)
authAndAccess(Container&& c, Index i)
{
authenticateUser();
return std::forward<Container>(c)[i];
}
同理,对于C++11,也要如此:1
2
3
4
5
6
7
8template<typename Container, typename Index>
auto
authAndAccess(Container&& c, Index i)
-> decltype(std::forward<Container>(c)[i])
{
authenticateUser();
return std::forward<Container>(c)[i];
}
对于类型为T的左值表达式,decltype返回T&
1 | int x = 0; // decltype(x) is int, decltype((x)) is int& |
f2返回值是int&,而且是局部变量的引用!小心该坑
要点:
decltype几乎总是产生和变量或表达式一模一样的类型- 对于类型为T的左值表达式,
decltype返回的类型总是T& C++14支持decltype(auto),像auto一样,从初始化对象中推导出类型,但是它使用decltype的规则进行推导