右值引用与移动语义与完美转发

右值引用

       右值

什么是右值,没有地址临时数据的我们称之为右值

右值引用与移动语义与完美转发_第1张图片

 我们无法对10、a+a、字符串取地址的值我们称之为右值。因为他们是临时数据,并不保存再内存中,所以我们右值没有地址,也无法被赋值(除const外,左值都可被赋值。

注意:

左值引用与右值引用可以交叉引用使用,但是需要加些东西。

int a=10;         //允许

int& ra=a;        //允许

int&& rr=10;      //允许

int&& _rr=a       //非法

int&& _rr=move(a) //允许

int& ra=10;      //非法

const int&ra=10;  //允许

move()

将传入的数据,不论是左值还是右值都返回一个右值。

移动语义

这里才是右值的深化,为什么怎么说呢?再c++11前我们的结构体返回不能接收栈内局部定义的变量,因为在销栈的时候我们的数据会连同这栈一同销毁,所以当返回的时候需要传值返回。

to_string就是最好的例子。

右值引用与移动语义与完美转发_第2张图片

 所以再C++11后我们的新增加了移动拷贝以及移动赋值。

这里我们先了解一个知识点,将亡值。

有哪些数据成为将亡值呢?

右值引用与移动语义与完美转发_第3张图片

 我们的右值引用可以对这些数据做引用。

右值引用与移动语义与完美转发_第4张图片

 为什么要要有移动语义呢?移动语义大大减少了对返回值的拷贝,高效的利用的将亡值数据。

右值引用与移动语义与完美转发_第5张图片

 右值引用与移动语义与完美转发_第6张图片

右值引用与移动语义与完美转发_第7张图片

 这样我们就对ret的资源充分的利用,而不是单纯的拷贝构造。多次的移动构造,成功的将ret对象指向数据空间转移到了receive对象。

完美转发

有一个值得注意的是,在右值应用后的数据会成为左值。假设完美vector存放的是string为元素的数组,这情况下就需要完美转发

模板中的&& 万能引用

何为万能引用,既可以引用左值也可以引用右值。

右值引用与移动语义与完美转发_第8张图片

void Fun(int& x) { cout << "左值引用" << endl; }
void Fun(const int& x) { cout << "const 左值引用" << endl; }
void Fun(int&& x) { cout << "右值引用" << endl; }
void Fun(const int&& x) { cout << "const 右值引用" << endl; }

template
void  PerfectForward(Ty&&x)
{
	Fun(x);
}

int main()
{
	PerfectForward(10);           // 右值
	int a;
	PerfectForward(a);            // 左值
	PerfectForward(std::move(a)); // 右值
	const int b = 8;
	PerfectForward(b);      // const 左值
	PerfectForward(std::move(b)); // const 右值
	return 0;
}

但是在进入PerfectForward函数后的,不论是说明值,在传入下一级函数后都属于左值数据。

 为了保持数据在传输过程中的数值类型(左右值)不发生改变,我们引入了完美转发的概念。

右值引用与移动语义与完美转发_第9张图片

经过x是什么值引用,什么类型

实际作用在于,容器存储数据的时候,如果元素是自定义类型,我们可以用完美转发保持右值的属性

template
struct ListNode
{
	ListNode* _next = nullptr;
	ListNode* _prev = nullptr;
	T _data;
};
template
class List
{
	typedef ListNode Node;
public:
	List()
	{
		_head = new Node;
		_head->_next = _head;
		_head->_prev = _head;
	}
	void PushBack(T&& x)
	{
		//Insert(_head, x);
		Insert(_head, std::forward(x));
	}
	void PushBack(const T& x)
	{
		Insert(_head, x);
	}

	void Insert(Node* pos, T&& x)
	{
		Node* prev = pos->_prev;
		Node* newnode = new Node;
		newnode->_data = std::forward(x); // 关键位置
		// prev newnode pos
		prev->_next = newnode;
		newnode->_prev = prev;
		newnode->_next = pos;
		pos->_prev = newnode;
	}
	void Insert(Node* pos, const T& x)
	{
		Node* prev = pos->_prev;
		Node* newnode = new Node;
		newnode->_data = x; // 关键位置
		// prev newnode pos
		prev->_next = newnode;
	}
	newnode->_prev = prev;
	newnode->_next = pos;
	pos->_prev = newnode;
private:
	Node* _head;
};
int main()
{
	List lt;
	lt.PushBack("1111");
	lt.PushFront("2222");
	return 0;
}

 右值引用与移动语义与完美转发_第10张图片

 每一层都需要被完美转发才能抵达最后的移动构造

 

 

你可能感兴趣的:(算法)