C++Effective Modern C++翻译(4)-条款3:了解decltype

条款3 了解decltype

decltype是一个诙谐之东西,给其一个变量名或是一个表达式,decltype会告诉您这变量名或是这个表达式的品类,通常,告诉您的结果以及你展望的凡同等的,但是有时的结果吗会见被您扒思考,开始摸索有参考资料进行研讨,或是在网上搜索答案。

 

咱于突出的例证开始,因为它的结果尚且是当我们预料之中的,和模板类型推导与auto类型推导相比(参见条款1以及条文2),decltype几乎连接连回到变量名或是表达式的类别而休见面进行其它的改

const int i = 0;           // decltype(i)是const int 
 bool f(const Widget& w);  // decltype(w)是const Widget& 
                           // decltype(f) 是 bool(const Widget&) 
struct Point { 
int x, y;                  // decltype(Point::x)是int 
};                         // decltype(Point::y)是int 
Widget w;                  // decltype(w) 是 Widget

if (f(w)) …                // decltype( f(w)) 是 bool 
template<typename T>       // std::vector的简单版本 
class vector { 
public: 
… 
T& operator[](std::size_t index); 
… 
}; 
vector<int> v;            // decltype(v)是vector<int> 
… 
if (v[0] == 0) …          // decltype(v[i]) 是 int&

 

在押,这没啊让人惊愕的。

当C++11挨,decltype的根本为此处在当函数模板的返路取决于参数类型的时节。例如,我们怀念如果描绘一个函数,它的参数有支持下标运算的容器与一个索引值,函数先对用户展开说明,然后回下标运算的结果,所以函数的回到路应该和下标运算的结果类型是一模一样的。

 

[]运算符作用在一个盖T为元素的容器上时常,通常返回T&,std::deque就是这么的,std::vector也几乎千篇一律,唯一的差是对于std::vecotr<bool>,[]运算符不回来一个bool&,相反的,它回到一个簇新的靶子,条款6拿解释马上是胡,但是要的凡念念不忘作用在容器上之[]运算符的回路取决于这个容器本身。

 

decltype让这宗事易得简单,这里是咱写的第一只版本,显示了用decltype推导返回路的办法,这个模板还好更精简一些,但是我们少先不考虑是:

template<typename Container, typename Index> //可以工作 
auto authAndAccess(Container& c, Index i)    // 但是能再精简 
-> decltype(c[i])                            // 一些 
{ 
authenticateUser(); 
return c[i]; 
}

函数名字前之auto和档次推导没有外的干,它暗示了C++11底追踪返回路(trailing
return
type)语义正于采用,例如:函数的返路将于参数列表的末尾声明(在->之后),追踪返回路

的优势是函数的参数能于回到路的宣示遭运用,例如,在authAndAccess中,我们用c和i来指定函数的归路,如果我们怀念使拿回到路声明在部数名叫于的前,就比如传统的函数一样,c和i是不能够吃用的,因为他们还尚无于声称。

 

用是宣称,authAndAccess返回[]运算符作用在容器上常之归路,和我们怀念要之同。

 

C++11兴推导单一语句之lambda的归来路,C++14恢宏了这个,使得lambda和所有函数(包括富含多修告句的函数)的回到路且可推导,这意味在C++14着我们得以看看略掉追踪返回路(trailing
return
type),只留下auto,在这种样式下的扬言中,auto意味着项目推导将见面生,详细的说,它象征编译器将会由函数的落实来演绎函数的回路:

template<typename Container, typename Index> // C++14支持 
auto authAndAccess(Container& c, Index i)    // 并不是十分 
{                                            // 正确 
authenticateUser(); 
return c[i];                                 // 从c[i]推导返回类型 
}

而是哪一样种C++的档次推导规则以见面叫运啊?模板的品类推导规则还是auto的,或者是decltype的?

 

或答案会略微被人咋舌,带有auto返回路的函数使用模板类型推导规则,尽管看起auto的型推导规则会更称这语义,但是模板类型推导规则及auto类型推导规则几乎是同样模型一样的,唯一的异是模板类型推导规则以冲大括如泣如诉的初始化式(braced
initializer)时会见破产。

 

既然如此这样的话,使用模板类型推导规则推导authAndAccess的归路是产生题目的,但是auto类型推导规则可不了多少,困难源自他们对左值表达式的拍卖。

 

譬如说咱事先讨论了的,大多数[]运算符作用在为T为因素的容器上时时回来一个T&,但是条款1说了以模板类型推导期间,初始化表达式的援部分用被忽视掉,考虑下的客户代码,使用了带有auto返回路(使用模板类型推导来演绎它的归来路)的authAndAccess:

std::deque<int> d; 
… 
authAndAccess(d, 5) = 10; //验证用户,返回d[5], 
                          // 并将10赋值给它; 
                          // 不会通过编译!

这里,d[5]回来了一个int&,但是对于authAndAccess函数,auto返回路的演绎将会见去丢引用部分,因此发生的归路是int,作为函数的回到路,int是一个右值,而地方的代码尝试将10与给一个int类型的右值,这当C++中是禁的,所以地方的代码无法透过编译。

 

问题来自我们以的是模板类型推导规则,它会丢初始化表达式中的援限定符。所以于这种景象下,我们怀念如果的凡decltype类型规则,decltype类型推导能允许我们保证authAndAccess返回的项目以及表述式c[i]种类是完全一致的。

 

C++规则的制定者(The guardians of
C++),预料到了以某种情形下档推导需要用decltype类型推导规则,所以于C++14中起了decltype(auto)说明符,这个刚刚开头看起或会见发出头矛盾(decltype和auto?),但实际他们是了合理的,auto说明了路需要吃演绎,decltype说明了decltype类型推导应该以演绎中被使用,因此authAndAccess的代码会是下边这样:

template<typename Container, typename Index> //C++14支持; 
decltype(auto)                               //能工作, 但是 
authAndAccess(Container& c, Index i)         //仍需要 
{                                            //改进  
authenticateUser();

return c[i]; 
}

今日authAndAccess返回的类别将见面和c[i]回去的类型完全一致,是当c[i]回一个T&时,authAndAccess也会见回一个T&,而当c[i]归来一个对象时,authAndAccess也会回来一个靶。

 

decltype(auto)的下并无囿于为函数的归来路,当您想要为此decltype类型推导来演绎初始化式时,你啊得十分便宜的施用她来声称一个变量。

Widget w; 
const Widget& cw = w; 
auto myWidget1 = cw;           // auto推导出的: 
                               // myWidget1类型是Widget 
decltype(auto) myWidget2 = cw; // decltype推导出的: 
                               // myWidget2类型是 
                               // const Widget&

 

而是自身晓得出少起事会烦你,一个凡为何authAndAccess仍欲改良,现在深受我们补充上立刻同样段吧。

 

咱重新看一样潮C++14版本下之authAndAccess函数声明:

template<typename Container, typename Index> 
decltype(auto) authAndAccess(Container& c, Index i);

容器是以一个左值的非常量引用传入的,因为归一个器皿中元素的援允许我们来改是容器,但当时代表我们无容许传递一个右值的容器到者函数中去,右值是无能为力绑定到一个左值的援上的(除非是一个的常量左值引用,但本例中莫是这般的)

 

无可否认,传递一个右值的器皿被authAndAccess是一个疆情况,一个右值的器皿,作为一个即对象将会见于蕴藏authAndAccess的函数调用的语句结束晚给损毁(would
typically be destroyed at the end of the statement containing the call
to
authAndAccess),这意味着容器中之一个要素的援(这便是authAndAccess函数返回的)将见面在调用语句的收时悬空,(and
that means that a reference to an element in that container (which is
typically what authAndAccess would return) would dangle at the end of
the statement that created
it)。然而,传递一个现对象到authAndAccess中是发道理的,一个客户或只是怀念使拷贝这个临时容器被的一个因素,例如:

std::deque<std::string> makeStringDeque(); // 工厂函数 
                                           //从makeStringDeque的函数值中拷贝 
                                           //容器的第五个元素 
auto s = authAndAccess(makeStringDeque(), 5);

支撑这种以方法表示我们需要修改c的声明,使得他得以又接受左值和右值,这代表c需要变成一个万会引用(universal
reference)(见条款26)

template<typename Container, typename Index> 
decltype(auto) authAndAccess(Container&& c, Index i);

于这模板里,我们不明了我们操作的容器是呀品种的,这又代表我们忽略了容器下标所对应之要素的路。利用传值方式传递一个不明不白之靶子,通常需要忍受不必要之正片,对象为分割的问题(见条款17),还有来自同事的笑话,但是因标准库中之事例(例如
std::string,std::vector和std::deque),这种情景下看起吧是合理的,所以我们坚持按值传递。

 

而今若是开的饶是创新模板的实现,结合条款27惨遭之警戒,使用std::forward来好

template<typename Container, typename Index> //C++14的 
decltype(auto)                               // 最终 
authAndAccess(Container&& c, Index i)        // 版本 
{ 
authenticateUser(); 
return std::forward<Container>(c)[i]; 
}

斯本子会不辱使命其余我们想只要就的,但是急需一个支持C++14之编译器,如果你未曾的话,你用采取一个C++11之版本,这和C++14本子相似,除了你需要团结标明出返回的类型

template<typename Container, typename Index> //C++11的 
auto                                         // 的最终 
authAndAccess(Container&& c, Index i)        // 版本

-> decltype(std::forward< Container>(c)[i]) 
{ 
authenticateUser(); 
return std::forward<Container>(c)[i]; 
}

任何一个值得对你多嘴的题材本身既标注在了当下同一条文的启幕处于了,decltype的结果几乎跟您所愿意的相同,这早已供不应求为惊讶了,说实话,你几乎不太可能遇到这个规则之例外情况,除非你是一个要命大之堆栈底实现者。

 

为毕亮decltype的行事,你用为你协调深谙一些不同寻常之景,大多数每当就本书里证实讨论四起会好的别扭,但是中间同样修能让咱更懂得decltype的采用。

 

本着一个变量称用decltype产生声明是变量时之路,但是即使像自己说之,有名字的凡左值表达式,但这绝非影响decltype的行事,因为对于比较变量名又扑朔迷离的左值表达式,decltype确保推导出的项目总是一个左值的援,这象征一旦一个左值表达式不同让变量名的类型T(That
is, if an lvalue expression other than a name has type
T),decltype推导出底类将见面是T&,这几无见面照成什么影响,因为大部分左值表达式的种类中通常含了一个左值引用的限定符,例如,返回左值的函数总是回到一个援。

 

此处出一个值得注意的地方,在

int x=0;

x是一个变量的讳,所以decltype(x)的结果是int,但是用名字x用括号包裹起来,”(x)”产生了一个于名字重新复杂的表达式,作为一个变量叫做,x是一个左值,C++同时定义了(x)也是一个左值,因此decltype((x))结果是int&,将一个变量用括号包裹起来改变了decltype最初的结果。

 

于C++11着,这就会会见吃丁小奇怪,但是结合C++14丁针对decltype(auto)的支撑后,你针对回语句之片段简的转移会影响至函数最终推导出底结果。

decltype( auto) f1() 
{ 
int x = 0; 
… 
return x;   // decltype(x) 是 int, 所以f1返回int 
} 
decltype(auto) f2() 
{ 
int x = 0; 
… 
return (x); // decltype((x)) 是int&, 所以f2返回int& 
}

瞩目到f2和f1不仅仅是归路上的差,f2回来的是一个有的变量的援,这种代码的结果是无定义的,你本不盼发生这种气象。

 

君得牢记的凡当您以decltype(auto)的时光,需要充分留意,一些关押起无关紧要的细节会影响到decltype(auto)推导出之结果,为了保险被演绎出的类别是若指望的,
可以采取条款4遭遇讲述的艺。

 

然而与此同时不要错过对全局的注目,decltype(无论是独立运用要跟auto一起行使)推导的结果也许有时候为人口惊叹,但是就并无会见常来,通常,decltype的结果与而所梦想的门类一样,尤其是当decltype应用在变量名的时候,因为于这种场面下,decltype做的就是是提供变量的扬言类型。

 

请记住:

  • decltype几乎总是回到变量名或是表达式的型而无会见展开任何的修改。
  • 对不同为变量名的左值表达式,decltype的结果连续T&。
  • C++14资了decltype(auto)的支持,比如auto,从其的初始化式中演绎类型,但利用decltype的演绎规则。