右值引用
右值
什么是右值,没有地址临时数据的我们称之为右值
我们无法对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就是最好的例子。
所以再C++11后我们的新增加了移动拷贝以及移动赋值。
这里我们先了解一个知识点,将亡值。
有哪些数据成为将亡值呢?
我们的右值引用可以对这些数据做引用。
为什么要要有移动语义呢?移动语义大大减少了对返回值的拷贝,高效的利用的将亡值数据。
这样我们就对ret的资源充分的利用,而不是单纯的拷贝构造。多次的移动构造,成功的将ret对象指向数据空间转移到了receive对象。
完美转发
有一个值得注意的是,在右值应用后的数据会成为左值。假设完美vector存放的是string为元素的数组,这情况下就需要完美转发
模板中的&& 万能引用
何为万能引用,既可以引用左值也可以引用右值。
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<class Ty>
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函数后的,不论是说明值,在传入下一级函数后都属于左值数据。
为了保持数据在传输过程中的数值类型(左右值)不发生改变,我们引入了完美转发的概念。
经过x是什么值引用,什么类型
实际作用在于,容器存储数据的时候,如果元素是自定义类型,我们可以用完美转发保持右值的属性
template<class T>
struct ListNode
{
ListNode* _next = nullptr;
ListNode* _prev = nullptr;
T _data;
};
template<class T>
class List
{
typedef ListNode<T> Node;
public:
List()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
void PushBack(T&& x)
{
//Insert(_head, x);
Insert(_head, std::forward<T>(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<T>(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<zjy::string> lt;
lt.PushBack("1111");
lt.PushFront("2222");
return 0;
}
每一层都需要被完美转发才能抵达最后的移动构造