C++(二)
C11 类型分类C11 以后进一步对类型进行了划分右值被细分为纯右值 (prvalue)和将亡值 (xvalue)。1. 纯右值 (pure value, 简称 prvalue)定义指那些字面值常量或求值结果相当于字面值或是一个不具名的临时对象。等价关系C11 中的纯右值概念划分等价于 C98 中的右值。常见示例字面值42、true、nullptr不具名的临时对象或运算结果str.substr(1, 2)、str1 str2传值返回的函数调用内置类型的运算如整型a、ba、a b等2. 将亡值 (expiring value, 简称 xvalue)定义指返回右值引用的函数的调用表达式以及转换为右值引用的转换函数的调用表达式。常见示例std::move(x)static_castX(x)3. 泛左值 (generalized lvalue, 简称 glvalue)定义泛左值是一个更宽泛的概念它包含了将亡值 (xvalue)和传统的左值 (lvalue)。C 引用折叠 (Reference Collapsing)1. 基本概念与规则背景C 中不能直接定义“引用的引用”例如int r i;会直接报错。但通过模板或typedef中的类型操作可以间接构成引用的引用。折叠规则当通过模板或 typedef 构成引用的引用时C11 给出了明确的折叠规则右值引用的右值引用折叠成右值引用( -)。所有其他组合均折叠成左值引用( , , -)。2. typedef 与函数模板中的折叠演示以下程序展示了在typedef和函数模板中引用折叠的具体表现// 由于引用折叠限定f1实例化以后总是一个左值引用templateclassTvoidf1(Tx){}// 由于引用折叠限定f2实例化后可以是左值引用也可以是右值引用templateclassTvoidf2(Tx){}intmain(){typedefintlref;typedefintrref;intn0;// --- typedef 中的折叠规则 ---lrefr1n;// r1 的类型是 int (左值引用的左值引用 - 左值引用)lrefr2n;// r2 的类型是 int (左值引用的右值引用 - 左值引用)rrefr3n;// r3 的类型是 int (右值引用的左值引用 - 左值引用)rrefr41;// r4 的类型是 int (右值引用的右值引用 - 右值引用)// --- 模板 f1 的折叠演示 ---// 没有折叠 - 实例化为 void f1(int x)f1int(n);// f1int(0); // 报错0为右值无法传参// 折叠 - 实例化为 void f1(int x)f1int(n);// f1int(0); // 报错// 折叠 - 实例化为 void f1(int x)f1int(n);// f1int(0); // 报错// 折叠 - 实例化为 void f1(const int x)f1constint(n);f1constint(0);// const左值引用可以正常引用右值// 折叠 - 实例化为 void f1(const int x)f1constint(n);f1constint(0);// --- 模板 f2 的折叠演示 ---// 没有折叠 - 实例化为 void f2(int x)// f2int(n); // 报错右值引用不可以引用左值f2int(0);// 折叠 - 实例化为 void f2(int x)f2int(n);// f2int(0); // 报错0为右值无法传参// 折叠 - 实例化为 void f2(int x)// f2int(n); // 报错f2int(0);return0;}万能引用 (Universal Reference)在像f2这样的函数模板中T x参数看起来像是一个普通的右值引用参数但由于引用折叠规则的存在它的实际行为非常特殊当传递左值时它就是左值引用。当传递右值时它就是右值引用。因此有些地方也把这种函数模板的参数叫做万能引用。推导原理在Function(T t)函数模板程序中其背后的推导逻辑如下实参为右值时假设实参是int右值模板参数T会被推导为int。结合引用折叠规则最终实例化出右值引用版本的函数。实参为左值时假设实参是int左值模板参数T会被推导为int。结合引用折叠规则int -int最终实例化出左值引用版本的函数。完美转发 (Perfect Forwarding)1. 为什么要使用完美转发在Function(T t)函数模板程序中传左值实例化以后是左值引用的Function函数。传右值实例化以后是右值引用的Function函数。存在的问题变量表达式都是左值属性。这意味着一个右值被右值引用绑定后右值引用变量即函数形参t本身的属性是左值。如果我们直接把t传递给下一层函数Fun那么匹配的永远都是左值引用版本的Fun函数。为了在转发过程中保持t对象原始的左右值属性我们需要使用完美转发。2.std::forward的实现原理std::forward本质是一个函数模板主要通过引用折叠的方式实现。其核心源码简化如下templateclass_Ty_Tyforward(remove_reference_t_Ty_Arg)noexcept{// 将参数无条件强转为 _Ty利用引用折叠规则还原原始类型returnstatic_cast_Ty(_Arg);}