左值引用&右值引用

目录

1. 什么是左值、右值

2. 什么是左值引用、右值引用

3. std::move() 移动语义

4. std::forward() 完美转发


1. 什么是左值、右值

左值可以取地址,例如位于等号左边 int a = 6 中的 a

        例如变量,左值在表达式结束后依然存在

右值不能取地址,例如位于等号右边 int a = 6 中的 6

        例如字面量或临时变量,通常表示一个即将被销毁的值

2. 什么是左值引用、右值引用

左值引用能指向左值,不能指向右值

例如 int &ref_a = a,而不能是 int &ref_a = 6;

因为右值没有地址,没办法被修改,而引用是变量的别名,实质上是一个变量,变量是有地址的

一个有地址,一个没地址,所以左值引用无法指向右值

但是,用const修饰后的左值引用是可以指向右值的,因为const左值引用不会修改指向值,

如 const int &ref_a = 6

这也是为什么要使用 const & 作为函数传参的原因之一

push_back(5);
void push_back(int &ref) {} // 报错
void push_back(const int &ref) {} // 不报错

右值引用的标志是 &&,两个取地址运算符,可以指向右值,不能指向左值

通过 std::move() 可以将左值转化为右值

int a = 5; // a 左值;5 右值
int &ref_l_a = a; // 左值引用
int &&ref_r_a = std::move(a); // move() 把左值转变成右值
ref_r_a = 6;

不管是左值引用还是右值引用,实质上还是引用,都可以通过取地址运算符拿到引用变量的地址

std::cout << "ref_l_a: " << &ref_l_a << std::endl; // ref_l_a: 0x7fffffffd9b4
std::cout << "ref_r_a: " << &ref_r_a << std::endl; // ref_r_a: 0x7fffffffd9b4

3. std::move() 移动语义

使用move()函数,将左值转化为右值

会触发移动构造函数,如果没有实现移动构造函数,会调用拷贝构造函数

#include <iostream>

class MyClass {
public:
  // 默认构造函数
  MyClass() {
    std::cout << "Default 默认构造" << std::endl;
  }

  // 拷贝构造函数
  MyClass(const MyClass &myClass) {
    std::cout << "Copy 拷贝构造" << std::endl;
  }

  // 移动构造函数
  // 如果此处不实现移动构造函数,使用move()函数的时候,会调用上面的拷贝构造函数
  MyClass(MyClass &&myClass) {
    std::cout << "Move 移动构造" << std::endl;
  }
};

int main() {
  MyClass myClass;
  MyClass myClass1 = myClass; // 调用拷贝构造函数
  MyClass myClass2 = std::move(myClass); // 调用移动构造函数

  return 0;
}

4. std::forward() 完美转发

完美转发(Perfect Forwarding)是 C++ 中一个重要的概念

主要用于模板编程,特别是在处理函数参数时

它的核心目的是在不改变参数的值类别(左值或右值)的情况下,将参数转发给另一个函数

即传入左值就是左值,传入右值就是右值

这在性能和效率上非常重要,因为它避免了不必要的拷贝和移动操作。

函数传参 void func(T&& arg) 中 T&& arg 的理解

T&& arg 是一个万能引用,也被称为转发引用

当函数模板的参数类型是 T&& 时,T 会根据传入参数的值类别来推导:

        如果传入的是左值,则 T 会推导为左值引用类型;

        如果传入的是右值,则 T 会推导为普通类型;

        由于 T 的推导机制,T&& arg 可以绑定到任意类型的值,无论是左值还是右值。

万能引用,或者说模板类型推导,是使用 std::forward() 的前提

#include <iostream>
#include <utility> // for std::forward

void process(int &x) { std::cout << "左值引用: " << x << std::endl; }

void process(int &&x) { std::cout << "右值引用: " << x << std::endl; }

template <typename T> void forwardToProcess(T &&arg) {
  // 完美转发 可以保留传入的参数类别
  process(std::forward<T>(arg)); 
  // 如果不使用 forward() 则不管传入的是左值还是右值,在函数内部,所有参数都是以左值的形式存在
  // process(arg);
}

int main() {
  int a = 10;
  forwardToProcess(a);  // 传入左值
  forwardToProcess(20); // 传入右值

  return 0;
}
&lt;think&gt;嗯,用户现在想了解C++中的左值左值引用引用的概念及区别。我需要先回顾一下这些基本概念,确保自己理解正确,然后再组织语言用中文清晰解释。 首先,左值的定义。左值通常的是有明确内存地址的对象,可以取地址,比如变量、类对象等。而则是临时的,无法取地址,比如字面量、临时对象或表达式结果。比如,int a = 5中的a是左值,5是。 接下来是左值引用引用左值引用&amp;符号,只能绑定左值,比如int&amp; b = a;而引用&amp;&amp;,只能绑定,比如int&amp;&amp; c = 5。不过,用户提供的引用内容中提到,当使用auto&amp;&amp;或模板参数推导时,可能会有引用折叠的情况,这时候引用可能推导为左值引用,比如当传递一个引用变量时,因为它有名字,所以被当作左值处理,这可能让用户容易混淆。 然后需要解释移动语义和完美转发,但用户的问题主要集中在概念和区别,可能需要简要提及这些高级特性,但重点还是放在基础区别上。根据引用中的例子,比如引用在传递时会被推导为左值,导致调用左值引用的函数,这说明理解引用折叠规则和类型推导很重要。 另外,用户提供的引用内容中提到了引用折叠的规则,比如T&amp;&amp;在类型推导时,如果传递的是左值,会被折叠为左值引用。例如,auto&amp;&amp; bb = a1,其中a1是引用变量,但因为它本身是左值,所以推导为左值引用。这点需要强调,因为引用变量本身是左值,只有表达式才是。 用户可能混淆的地方在于引用变量虽然是引用类型,但其本身作为具名变量是左值,所以传递给函数时会被当作左值处理。例如,在引用1中的例子,传递引用变量t时,t是左值,所以调用左值引用的函数。这可能涉及到移动语义中的std::move,将左值转换为引用。 总结起来,回答的结构应该包括定义、区别、示例代码以及常见误区。需要确保每个概念都清晰,并且对比左值引用引用的使用场景和语法差异。同时,引用折叠和类型推导的规则需要简明解释,但不过于深入以免让初学者困惑。 最后,检查是否有遗漏点,比如const对引用的影响,或者移动构造函数的作用,但根据用户的问题,可能不需要深入这些高级主题,除非必要。确保回答符合用户的需求,帮助他们建立基础概念,并能够区分左值及其引用的不同。&lt;/think&gt;--- ### C++左值引用类型全解析 --- #### 一、核心定义与关系图谱 ```mermaid graph TD A[表达式] --&gt; B[左值] A --&gt; C[] B --&gt; D[可获取地址] C --&gt; E[临时/字面量] B --&gt; F[可修改] C --&gt; G[不可直接修改] H[引用类型] --&gt; I[左值引用 &amp;] H --&gt; J[引用 &amp;&amp;] ``` --- #### 二、基础概念对比 | **类型** | **特征** | **生命周期** | **典型示例** | |----------------|---------------------------------------------|--------------------------|-------------------------| | **左值** | 具名对象,可获取地址 | 持续到作用域结束 | `int a = 10;` 中的`a` | | **** | 匿名临时,不可取地址 | 立即销毁(可能延长) | `5`, `a+3`的结果 | | **左值引用** | 绑定左值引用(`&amp;`) | 与绑定对象同步 | `int &amp;b = a;` | | **引用** | 绑定引用(`&amp;&amp;`) | 接管资源所有权 | `int &amp;&amp;c = 10;` | --- #### 三、关键区别详解 ##### 1. **左值 vs ** - **地址访问性** ```cpp int x = 5; int* p1 = &amp;x; // ✅ 左值可获取地址 int* p2 = &amp;5; // ❌ 编译错误:无法取地址 ``` - **生命周期控制** ```cpp std::string s1 = &quot;hello&quot;; // 左值s1持续存在 std::string s2 = s1 + &quot; world&quot;; // 临时对象立即销毁 ``` ##### 2. **左值引用 vs 引用** - **绑定规则** ```cpp int a = 10; int &amp;lr = a; // ✅ 左值引用绑定左值 int &amp;&amp;rr1 = 10; // ✅ 引用绑定 int &amp;&amp;rr2 = a; // ❌ 编译错误:引用不能绑定左值 ``` - **移动语义实现** ```cpp class MyString { public: // 移动构造函数(接管资源) MyString(MyString&amp;&amp; other) noexcept { data = other.data; other.data = nullptr; // 原对象置空[^1] } }; ``` --- #### 四、引用折叠规则(重点) 当出现**未定引用类型**时(如模板参数推导、`auto&amp;&amp;`),通过引用折叠确定最终类型: | **原始类型** | **推导类型** | **折叠规则** | **示例** | |--------------|-------------|--------------------------|----------------------------| | `T&amp; &amp;` | `T&amp;` | 双左值引用折叠为左值引用 | `auto&amp;&amp; a = x;`(x为左值) | | `T&amp; &amp;&amp;` | `T&amp;` | 混合引用折叠为左值引用 | `auto&amp;&amp; b = get_lref();` | | `T&amp;&amp; &amp;` | `T&amp;` | 混合引用折叠为左值引用 | `auto&amp;&amp; c = std::move(y);` | | `T&amp;&amp; &amp;&amp;` | `T&amp;&amp;` | 双引用折叠为引用 | `auto&amp;&amp; d = 10;` | **示例分析**(来自引用[2]): ```cpp int&amp;&amp; a1 = 1; // a1是引用(绑定) auto&amp;&amp; bb = a1; // bb推导为int&amp;(a1是具名引用&rarr;视为左值) auto&amp;&amp; bb1 = 2; // bb1推导为int&amp;&amp;(绑定) int a2 = 1; auto&amp;&amp; cc = a2; // cc推导为int&amp;(a2是左值) ``` --- #### 五、实战场景与技巧 ##### 1. **完美转发(Perfect Forwarding)** ```cpp template&lt;typename T&gt; void wrapper(T&amp;&amp; arg) { // 万能引用(可绑定/) process(std::forward&lt;T&gt;(arg)); // 保留原始类型 } // 调用示例 int x = 5; wrapper(x); // 传递左值&rarr;调用process(int&amp;) wrapper(10); // 传递&rarr;调用process(int&amp;&amp;) ``` ##### 2. **移动语义优化** ```cpp std::vector&lt;int&gt; createBigData() { std::vector&lt;int&gt; tmp(1000000); return tmp; // 触发移动构造(避免深拷贝)[^1] } auto v = createBigData(); // 高效资源转移 ``` --- #### 六、常见误区与调试 - **引用变量是左值** ```cpp int&amp;&amp; rref = 10; int&amp; lr = rref; // ✅合法:rref是具名变量&rarr;视为左值 ``` - **`const`修饰符的影响** ```cpp const int&amp;&amp; crr = 10; crr = 20; // ❌错误:常量引用不可修改 ``` --- #### 七、总结对比表 | **特征** | **左值** | **** | **左值引用** | **引用** | |------------------|------------------------|------------------------|-----------------------|-----------------------| | **地址可获取性** | ✅ | ❌ | 继承绑定对象属性 | 继承绑定对象属性 | | **主要用途** | 存储持久数据 | 临时计算/资源转移 | 别名/避免拷贝 | 移动语义/完美转发 | | **典型绑定对象** | 变量、数组元素 | 字面量、返回临时对象 | 左值 | | ---
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值