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

 左值(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右值引用(一看即懂)

  • 10
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值