C++(二)
2026/5/15 6:56:14 网站建设 项目流程

C++11 类型分类

C++11 以后,进一步对类型进行了划分,右值被细分为纯右值 (prvalue)将亡值 (xvalue)

1. 纯右值 (pure value, 简称 prvalue)
  • 定义:指那些字面值常量,或求值结果相当于字面值,或是一个不具名的临时对象。
  • 等价关系:C++11 中的纯右值概念划分等价于 C++98 中的右值。
  • 常见示例
    • 字面值:42truenullptr
    • 不具名的临时对象或运算结果:str.substr(1, 2)str1 + str2
    • 传值返回的函数调用
    • 内置类型的运算:如整型aba++a + b
2. 将亡值 (expiring value, 简称 xvalue)
  • 定义:指返回右值引用的函数的调用表达式,以及转换为右值引用的转换函数的调用表达式。
  • 常见示例
    • std::move(x)
    • static_cast<X&&>(x)
3. 泛左值 (generalized lvalue, 简称 glvalue)
  • 定义:泛左值是一个更宽泛的概念,它包含了将亡值 (xvalue)和传统的左值 (lvalue)

C++ 引用折叠 (Reference Collapsing)

1. 基本概念与规则
  • 背景:C++ 中不能直接定义“引用的引用”,例如int& && r = i;会直接报错。但通过模板typedef中的类型操作可以间接构成引用的引用。
  • 折叠规则:当通过模板或 typedef 构成引用的引用时,C++11 给出了明确的折叠规则:
    • 右值引用的右值引用折叠成右值引用(&& &&->&&)。
    • 所有其他组合均折叠成左值引用(& &,& &&,&& &->&)。

2. typedef 与函数模板中的折叠演示

以下程序展示了在typedef和函数模板中,引用折叠的具体表现:

// 由于引用折叠限定,f1实例化以后总是一个左值引用template<classT>voidf1(T&x){}// 由于引用折叠限定,f2实例化后可以是左值引用,也可以是右值引用template<classT>voidf2(T&&x){}intmain(){typedefint&lref;typedefint&&rref;intn=0;// --- typedef 中的折叠规则 ---lref&r1=n;// r1 的类型是 int& (左值引用的左值引用 -> 左值引用)lref&&r2=n;// r2 的类型是 int& (左值引用的右值引用 -> 左值引用)rref&r3=n;// r3 的类型是 int& (右值引用的左值引用 -> 左值引用)rref&&r4=1;// r4 的类型是 int&& (右值引用的右值引用 -> 右值引用)// --- 模板 f1 的折叠演示 ---// 没有折叠 -> 实例化为 void f1(int& x)f1<int>(n);// f1<int>(0); // 报错,0为右值,无法传参// 折叠 -> 实例化为 void f1(int& x)f1<int&>(n);// f1<int&>(0); // 报错// 折叠 -> 实例化为 void f1(int& x)f1<int&&>(n);// f1<int&&>(0); // 报错// 折叠 -> 实例化为 void f1(const int& x)f1<constint&>(n);f1<constint&>(0);// const左值引用可以正常引用右值// 折叠 -> 实例化为 void f1(const int& x)f1<constint&&>(n);f1<constint&&>(0);// --- 模板 f2 的折叠演示 ---// 没有折叠 -> 实例化为 void f2(int&& x)// f2<int>(n); // 报错,右值引用不可以引用左值f2<int>(0);// 折叠 -> 实例化为 void f2(int& x)f2<int&>(n);// f2<int&>(0); // 报错,0为右值,无法传参// 折叠 -> 实例化为 void f2(int&& x)// f2<int&&>(n); // 报错f2<int&&>(0);return0;}

万能引用 (Universal Reference)

在像f2这样的函数模板中,T&& x参数看起来像是一个普通的右值引用参数,但由于引用折叠规则的存在,它的实际行为非常特殊:

  • 当传递左值时,它就是左值引用
  • 当传递右值时,它就是右值引用

因此,有些地方也把这种函数模板的参数叫做万能引用

推导原理

Function(T&& t)函数模板程序中,其背后的推导逻辑如下:

  1. 实参为右值时
    假设实参是int右值,模板参数T会被推导为int。结合引用折叠规则,最终实例化出右值引用版本的函数。
  2. 实参为左值时
    假设实参是int左值,模板参数T会被推导为int&。结合引用折叠规则(int& &&->int&),最终实例化出左值引用版本的函数。

完美转发 (Perfect Forwarding)

1. 为什么要使用完美转发?

Function(T&& t)函数模板程序中:

  • 传左值实例化以后是左值引用的Function函数。
  • 传右值实例化以后是右值引用的Function函数。

存在的问题
变量表达式都是左值属性。这意味着一个右值被右值引用绑定后,右值引用变量(即函数形参t)本身的属性是左值。如果我们直接把t传递给下一层函数Fun,那么匹配的永远都是左值引用版本的Fun函数。

为了在转发过程中保持t对象原始的左右值属性,我们需要使用完美转发。

2.std::forward的实现原理

std::forward本质是一个函数模板,主要通过引用折叠的方式实现。其核心源码简化如下:

template<class_Ty>_Ty&&forward(remove_reference_t<_Ty>&_Arg)noexcept{// 将参数无条件强转为 _Ty&&,利用引用折叠规则还原原始类型returnstatic_cast<_Ty&&>(_Arg);}

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询