《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
的规则进行推导