左值,右值,左值引用,右值引用

介绍

左值(Lvalue)

左值是可以出现在赋值语句左边的表达式。具有以下特征:

  • 有持久的内存地址
  • 可以被取地址
  • 可以被赋值
int x = 10;  // x是左值  
int* ptr = &x;  // 可以取x的地址  
x = 20;  // 可以被赋值

右值(Rvalue)

右值是只能出现在赋值语句右边的表达式。具有以下特征:

  • 临时的
  • 不可取地址
  • 不可被赋值
int y = x + 5;  // (x + 5)是右值  
int z = 10;     // 10是右值

左值引用(Lvalue Reference)

左值引用是传统的引用,使用 & 符号声明。

  • 只能绑定到左值
  • 可以读写原始对象
  • 不能绑定到右值(C++11之前)
int x = 10;  
int& ref = x;  // ref是x的左值引用  
ref = 20;      // 通过引用修改原值

右值引用(Rvalue Reference)

右值引用是C++11引入的新特性,使用 && 符号声明。主要用于移动语义和完美转发。

  • 可以绑定到右值
  • 主要用于移动语义和完美转发
  • 可以“窃取“临时对象的资源
int&& rref = 10;  // 右值引用

右值引用的主要应用场景

移动语义

class MyString {  
public:  
    // 移动构造函数  
    MyString(MyString&& other) noexcept {  
        // 直接转移资源,避免深拷贝  
    }  
};

完美转发

template<typename T>  
void wrapper(T&& arg) {  
    // 完美转发参数  
    foo(std::forward<T>(arg));  
}

左值和右值的转换

int x = 10;  
int&& rref = std::move(x);  // 将左值x转换为右值引用

实际应用示例

#include <iostream>  
#include <utility>  

void processValue(int& x) {  
    std::cout << "Lvalue reference" << std::endl;  
}  

void processValue(int&& x) {  
    std::cout << "Rvalue reference" << std::endl;  
}  

int main() {  
    int a = 10;  
    processValue(a);        // 调用左值引用版本  
    processValue(10);       // 调用右值引用版本  
    processValue(std::move(a));  // 调用右值引用版本  
    return 0;  
}

总结

  • 左值:有标识符,可寻址
  • 右值:临时的,不可寻址
  • 左值引用:传统引用,绑定左值
  • 右值引用:C++11特性,支持移动语义和完美转发
  • std::move() 可以将左值转换为右值引用
  • 右值引用支持移动构造和移动赋值,减少不必要的内存拷贝

用法

左值,右值

C++任何一个对象要么是左值,要么是右值 int i = 10,i 和 10 都是对象,i是左值,10是右值;

左值:拥有地址属性的对象就叫左值,左值来源于c语言的说法,能放在“=”左面的就是左值,注意,左值也可以放在“=”右面。

右值:没有地址属性的对象就叫做右值,注意,右值绝对不可以放在等号左面

有地址属性,就代表可以操作地址,没有地址属性,就无法操作操作地址;

一般来说, 判断一个对象是左值还是右值,就看对象有没有地址属性。

比如临时对象,就都是右值,临时对象没有地址属性,无法操作地址。 注意:左值也可以放在“=”右面,但右值绝对不可以放在等号左面

小测验:

#include <iostream>

int main() {
    int i = 10;
    int i2 = (i + 1);   // i + 1 临时对象 右值
    ++i = 200;          // ++i 先给i加1,然后返回i,i是有地址的,左值
    i++;                // i++ 先返回一个临时变量,临时变量的值 = i的值,然后临时变量的值 + 1
                        // 返回的是临时变量,当然无法使用地址
    return 0;
}

引用分类

普通左值引用:就是一个对象的别名,只能绑定左值,无法绑定常量对象

#include <iostream>

// 因为引用相当于别名,如果这里可以绑定的话,
// 我们只要修改refI的值,那么i的值可以绕过这个const修饰符而被修改,那么const就没有意义了

int main() {
    const int i = 100;
    int& refI = i;   // 非法,左值引用不允许绑定常量对象
    refI = 200;

    int j = 100;
    int& refJ = j;  // 合法
    
    return 0;
}

const 左值引用:可以对常量起别名,可以绑定左值和右值

#include <iostream>


int main() {
    const int i = 100;
    const int& refI = i;            // 绑定左值 合法
    const int& refI1 = (i + 1);     // 绑定右值 合法;

    return 0;
}

右值引用:只能绑定右值的引用

#include <iostream>

// 右值引用 只能绑定右值
int main() {
    int i = 100;
    int&& rrefI = 200;   // 右值引用,绑定右值合法
    int&& refI1 = i;     // 右值引用,绑定左值不合法

    return 0;
}

左值引用与右值引用的区别?右值引用的意义?

https://www.bilibili.com/video/BV1eN4y1R7Me?spm_id_from=333.788.videopod.sections&vd_source=cb02f779bd17a3aad9801e0c4464dfc9