C++11右值引用

文章详细介绍了C++中的左值和右值的概念,以及左值引用和右值引用的区别和使用场景。左值可以被修改并取地址,右值则通常是不可修改的数据。C++11引入的右值引用用于优化性能,通过移动语义避免不必要的拷贝。完美转发则是一种技术,用于在函数传递参数时保持原参数类型,支持左值和右值的通用处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

概念

左值和右值

  • 左值

左值是一个可以被取地址、修改的对象或变量,其具有固定的内存地址。

左值可以出现在赋值语句的左边,因为它们表示一个可被修改的存储位置。

int a = 1;	// a就是一个左值
  • 右值

右值是那些不能被取地址、只能读取的数据。

右值通常包括常量、表达式结果、字符串字面量和临时变量(传值返回的函数返回值)等。

右值出现在赋值语句中的右边,计算得到某个数值或数据,再赋给左值。

int x = 1, y = 2;		//1、2是右值
int a = x + y;			//(x + y) 是右值
string name = "yyjs";	//"yyjs"是右值

左值引用和右值引用

    C++11新特性 本质上都是进行取别名

  • 左值引用

&符号表示,可以绑定到一个左值,因为它们都可取地址

int a = 1;
int &ref_a = a; // 左值引用
  • 右值引用

&&符号表示,只能绑定到一个右值(临时变量、表达式、字面量等)

int &&rr1 = 1; 	// 右值引用
double&& rr2 = 1.2 + 2.9;

值得一提的是对于右值引用,如rr1、rr2。它们给右值取别名后,会分配特定的存储空间,通过右值引用,可以取地址,可以被修改。使用const修饰右值引用,禁止修改。

  • const左值引用

左值引用不能引用右值,右值是临时的、不可修改的。不能将权限放大

const左值引用可以引用右值,保证数据不会修改

void show(const string& str)
{
    cout << str << endl;
}
int main
{
    show("12345");//使用右值传参
}
  • move()

被move后的左值能够赋值给右值引用

int a = 1;
//int &&rr2 = a; error
int &&rr2 = std::move(a); 

使用

左值引用

我们通常使用左值引用作为参数返回值

void func1(const string& s){}
string& get(){}

都是为了减少(深)拷贝的消耗,提高效率


短板:但是当函数返回对象是一个局部变量 ,出了函数作用域就不存在了,就不能使用左值引用返回,只能传值返回。

string iTos(int value)
{
    string str;
    ...
    return str;
}
string s = iTos(123);

在这里插入图片描述

右值引用和移动语义

来解决左值引用的短板,提高效率

如果在string类中增加

移动构造函数
class string
{
public:
	// s1.swap(s2)
	void swap(string& s)
	{
		::swap(_str, s._str);
		::swap(_size, s._size);
		::swap(_capacity, s._capacity);
	}

	// 移动构造
	string(string&& s)
		:_str(nullptr)
		, _size(0)
		, _capacity(0)
	{
		swap(s);
	}
	。。。
private:
	char* _str;
	size_t _size;
	size_t _capacity; // 不包含最后做标识的\0
};

对于iTos()的使用,调用到移动构造。

string s = iTos(123);

在这里插入图片描述

    对于s的创建调用的是其构造函数。iTos()的返回值string对象是一个“将亡值”,即将消耗的对象。编译器会进行识别,并且调用string的移动构造函数string(string&& s){},函数内通过swap()将资源进行转移,从而避免深拷贝的消耗。

移动赋值
// 移动赋值
string& operator=(string&& s)
{
    swap(s);
    return *this;
}
string s;
s = iTos(123);//调用移动赋值

同理

move()
string s1("hello today~");
string s3 = std::move(s1);

同理,s1的资源通过右值引用,移动构造转移给了s3,并且s1为空。

完美转发

万能引用

模板中,&&符号代表万能引用,既能接收左值又能接收右值

template <class T>
void PrefectForward(T&& t) {}

在这里插入图片描述

右值被引用之后会被储存到特定的位置,而这个储存位置在函数参数中是可以被取地址和修改的,因此实际上已经退化为左值了

如果想在万能引用中保持右值属性,就需要完美转发

完美转发

要想在参数传递过程中保持其原有的属性,需要在传参时调用,forward()函数

template<class T>
void PerfectForward(T&& t)
{
	func(std::forward<T>(t));
}

在这里插入图片描述

完美转发在传参的过程中保留对象原生类型属性

使用示例:

template<class T>
struct ListNode
{
	T _data;
	ListNode* _next = nullptr;
	ListNode* _prev = nullptr;
};
template<class T>
class list
{
	typedef ListNode<T> node;
public:
    。。。
	// 右值引用版本
	void push_back(T&& x)
	{					//完美转发
		insert(_head, std::forward<T>(x));
	}
    
	// 右值引用版本
	void insert(node* pos, T&& x)
	{
		node* prev = pos->_prev;
		node* newnode = new node;
       //完美转发,同时会去调用T类的移动构造函数(如果有)
		newnode->_data = std::forward<T>(x);

		prev->_next = newnode;
		newnode->_prev = prev;
		newnode->_next = pos;
		pos->_prev = newnode;
	}
private:
	node* _head;
};
list<string> lt;
lt.push_back("world");//右值

    🦀🦀观看~~

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值