C++左值、右值、左值引用、右值引用

一时失言乱红尘 2023-01-13 06:23 276阅读 0赞

左值(lvalue)和右值(rvalue)

左值(lvalue):locator value,存储在内存中、有明确的的地址(可寻址)的数据

能够取地址,有名字的值就是左值

  1. //左值引用
  2. int a=10;
  3. int &a1=a;//正确,a为左值类型,因为可以我们找到他的地址
  4. int &a=10;//错误,左值引用不能引用一个右值类型的常量,(10是常量,常量为右值)

编译器允许我们对左值建立引用,但不允许对右值建立引用。除非使用常量左值引用操作右值:

  1. int num = 10;
  2. const int &b = num;
  3. const int &c = 10;

也就是说常量左值引用即可以操作左值,也可以操作右值。

右值(rvalue):read value, 不能取地址,无名字的值就是右值。

举个例子:

int a = b+c, a 就是左值,其有变量名为a,通过&a可以获取该变量的地址;

表达式b+c、函数int fun()的返回值是右值,在其被赋值给某一变量前,我们不能通过变量名找到它,&(b+c)这样的操作则不会通过编译。

  1. //右值引用
  2. int &&a=10;//正确,右值引用一个右值类型
  3. a = 100;
  4. cout << a << endl;//100
  5. int var = 10;
  6. int&& var1 = var;//错误,因为无法将右值引用绑定到左值上
  7. //右值引用绝对不能引用左值类型的,加上const也不行,这点是和左值引用不同的地方
  8. const int&& var1 = var//照样报错,加上const也不行
  9. //非引用返回的函数返回的都是右值,引用返回的函数返回的是左值
  10. int fun1() { }
  11. int &fun2() { }
  12. int main()
  13. {
  14. int& z = fun1();//左值引用,报错
  15. const int& z1 = fun1();//正确
  16. int&& z2 = fun1();//正确
  17. int& z = fun2();//左值引用,正确
  18. const int& z1 = fun2();//延长生命期的左值引用,正确
  19. int&& z2 = fun2();//报错,右值引用不能绑定左值
  20. const int&& z2 = fun2();//报错
  21. }

常量右值引用:

  1. const int&& a = 10;//编译器不会报错

但这种定义出来的右值引用并无实际用处:

一方面,右值引用主要用于移动语义和完美转发,其中需要有修改右值的权限;

另一方面,常量右值引用的作用就是引用一个不可修改的右值,这些工作完全可以交给常量左值引用。

C++左值引用和右值引用













































引用类型 可以引用的值类型 使用场景
非常量左值 常量左值 非常量右值 常量右值
非常量左值引用 Y N N N
常量左值引用 Y Y Y Y 常用于类中构建拷贝构造函数
非常量右值引用 N N Y N 移动语义、完美转发
常量右值引用 N N Y Y 无实际用途

纯右值和将亡值

C++98中的右值就是纯右值,纯右值指的是临时变量值,不跟对象关联的字面量值

  • 临时变量值:非引用返回的函数返回值、表达式等。
  • 不跟对象关联的字面量值:例如true、2、”Hello World”等。

C++11对C++98中的右值进行了扩充。在C++11中右值又分为纯右值(prvalue,Pure Rvalue)将亡值(xvalue,eXpiring Value)。其中纯右值的概念等同于我们在C++98标准中右值的概念,指的是临时变量和不跟对象关联的字面量值;

将亡值则是C++11新增的跟右值引用相关的表达式,这样表达式通常是将要被移动的对象(移为他用),比如:

  • 返回右值引用T&&的函数返回值
  • std::move的返回值
  • 转换为T&&的类型转换函数的返回值

将亡值可以理解为通过“盗取”其他变量内存空间的方式获取到的值。在确保其他变量不再被使用、或即将被销毁时,通过“盗取”的方式可以避免内存空间的释放和分配,能够延长变量值的生命期。

一般情况下,右值引用是不能引用左值的,但是可以使用std::move()函数将左值强制转换为右值

  1. int a;
  2. int &&r1 = c; // 编译失败
  3. int &&r2 = std::move(a); // 编译通过
  4. #include <iostream>
  5. #include <vector>
  6. #include <string>
  7. #include<utility>
  8. using namespace std;
  9. class A
  10. {
  11. public:
  12. A() {
  13. a = 666;
  14. cout << "构造A" << endl;
  15. }
  16. A(const A&a) {
  17. cout << "拷贝构造A" << endl;
  18. }
  19. ~A() {
  20. cout << "析构A" << endl;
  21. }
  22. private:
  23. int a;
  24. };
  25. int main(void) {
  26. int a = 6;
  27. int &&b = std::move(a);
  28. cout << "b = "<<++b << endl;//b=7
  29. cout << "a = "<<a << endl;//a=7
  30. string s = "hello";
  31. vector<string> vec;
  32. //调用常规的拷贝构造函数,新建字符串,拷贝数据
  33. vec.push_back(s);
  34. cout << "在拷贝之后:"<< s << endl;
  35. //调用移动构造函数,掏空了s
  36. vec.push_back(std::move(s));
  37. cout << "在移动之后:" << s << endl;
  38. A aa;
  39. vector<A> vec_A;
  40. vec_A.emplace_back(aa);
  41. //cout << "移动构造:" << endl;
  42. //调用移动构造函数,掏空了s
  43. //vec.emplace_back(std::move(aa));
  44. return 0;
  45. }

C++11 左值、右值、右值引用详解

c++ 之 std::move 原理实现与用法总结

C++11右值引用(一看即懂)

发表评论

表情:
评论列表 (有 0 条评论,276人围观)

还没有评论,来说两句吧...

相关阅读

    相关 C++

    左值(lvalue)右值(rvalue) 在C++11中所有的值必属于左值、右值两者之一,可以取地址的、有名字的就是左值,反之,不能取地址的、没有名字的就是右值。