C++11:左值右值左值引用右值引用有什么区别?

Posted 木大白易

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++11:左值右值左值引用右值引用有什么区别?相关的知识,希望对你有一定的参考价值。

左值(lvalue)和右值(rvalue)

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

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

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

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

int num = 10;
const int &b = num;
const int &c = 10;

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

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

举个例子:
int a = b+c, a 就是左值,其有变量名为a,通过&a可以获取该变量的地址;
表达式b+c、函数int fun()的返回值是右值,在其被赋值给某一变量前,我们不能通过变量名找到它,&(b+c)这样的操作则不会通过编译。

//右值引用
int &&a=10;//正确,右值引用一个右值类型
a = 100;
cout << a << endl;//100
 
int var = 10;
int&& var1 = var;//错误,因为无法将右值引用绑定到左值上
//右值引用绝对不能引用左值类型的,加上const也不行,这点是和左值引用不同的地方
const int&& var1 = var//照样报错,加上const也不行
 
//非引用返回的函数返回的都是右值,引用返回的函数返回的是左值
int fun1()  
int &fun2()  
int main()

	int& z = fun1();//左值引用,报错
	const int& z1 = fun1();//正确
	int&& z2 = fun1();//正确
 
    int& z = fun2();//左值引用,正确
	const int& z1 = fun2();//延长生命期的左值引用,正确
	int&& z2 = fun2();//报错,右值引用不能绑定左值
	const int&& z2 = fun2();//报错

常量右值引用:

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

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

一方面,右值引用主要用于移动语义和完美转发,其中需要有修改右值的权限;
另一方面,常量右值引用的作用就是引用一个不可修改的右值,这些工作完全可以交给常量左值引用。

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

纯右值和将亡值

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()函数将左值强制转换为右值

int a;
int &&r1 = c;             // 编译失败
int &&r2 = std::move(a);  // 编译通过
#include <iostream>
#include <vector>
#include <string>
#include <utility>
using namespace std;
 
class A

public:
	A() 
		a = 666;
		cout << "构造A" << endl;
	
	A(const A&a) 
		cout << "拷贝构造A" << endl;
	
	~A() 
		cout << "析构A" << endl;
	
 
private:
	int a;
;
 
int main(void) 
	
	int a = 6;
	int &&b = std::move(a);
	cout << "b = "<<++b << endl;//b=7
	cout << "a = "<<a << endl;//a=7
 
	string s = "hello";
	vector<string> vec;
	//调用常规的拷贝构造函数,新建字符串,拷贝数据
	vec.push_back(s);
	cout << "在拷贝之后:"<< s << endl;
	
	//调用移动构造函数,掏空了s
	vec.push_back(std::move(s));
	cout << "在移动之后:" << s << endl;
 
	A aa;
	vector<A> vec_A;
	vec_A.emplace_back(aa);
 
	//cout << "移动构造:" << endl;
	//调用移动构造函数,掏空了s
	//vec.emplace_back(std::move(aa));
 
 
	return 0;

推荐阅读

C++11 左值、右值、右值引用详解
c++ 之 std::move 原理实现与用法总结

以上是关于C++11:左值右值左值引用右值引用有什么区别?的主要内容,如果未能解决你的问题,请参考以下文章

C++11:左值右值左值引用右值引用

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

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

C++11 ——— 右值引用和移动语义

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

[转]C++11 左值右值右值引用详解