什么是右值 左值?
C++中所有的表达式和变量,要么是右值要么是左值。那什么是左值、右值呢?通俗上来说,左值就是可以被多次重复使用的值,而右值则是一个临时的对象,可能在使用之后便消亡了。我们来看一个简单的示例:
int a = 1
这里a就是一个左值,它不仅仅是在左边(感觉就是可以这样理解),他还可以在下面被调用,比方说我们下面可以把a赋值给另一个变量A,所以这里的a是一个左值。但是,等号右边的1就不一样了,他是一个字面量,在我们使用它给a复制之后便消亡了。
在C++11之前,右值是不能够被引用的,其最大限度是可以用常量来绑定一个右值,如:const int &a = 1
在C++11之后实现了对右值的引用
左值和右值的语法符号
左值的声明符号是‘&’,而右值的声明符号为‘&&’
下面我们看一段示例代码:
void process_value(int& i) { std::cout << "LValue processed: " << i << std::endl; } void process_value(int&& i) { std::cout << "RValue processed: " << i << std::endl; } int main() { int a = 0; process_value(a); process_value(1); }
其运行结果如下:
LValue processed: 0 RValue processed: 1
通过这个示例我们发现字面量1被当作右值处理,因而使用了右值引用重载函数。
但是如果一个临时对象通过了一个接受右值的函数传递给另一个函数时,它就变成了左值,在传递的过程中,这个对象被再次使用,因而又变成了左值。
示例程序:
void process_value(int& i) { std::cout << "LValue processed: " << i << std::endl; } void process_value(int&& i) { std::cout << "RValue processed: " << i << std::endl; } void forward_value(int&& i) { process_value(i); } int main() { int a = 0; process_value(a); process_value(1); forward_value(2); }
运行结果:
LValue processed: 0
RValue processed: 1
LValue processed: 2
移动构造函数
在构造函数中,经常会出现资源的浪费,比如说我们在一个函数内部,将实参传入函数之后,将其赋值之后便不再使用实参,这时为了节约资源就可以使用右值引用来构造移动构造函数:
class A_2 { public: A_2() :m_ptr(new int(0)) { std::cout << "construct" << std::endl; } A_2(const A_2& a):m_ptr(new int(*a.m_ptr)) //深拷贝 { std::cout << "copy construct" << std::endl; } A_2(A_2&& a) :m_ptr(a.m_ptr) { a.m_ptr = nullptr; std::cout << "move construct:" << std::endl; } ~A_2() { std::cout << "destruct" << std::endl; delete m_ptr; } private: int* m_ptr; }; A_2 Get(bool flag) { A_2 a; A_2 b; if (flag) return a; else return b; } int right_value_ref2() { A_2 a = Get(false); return 0; }
这段代码保证了程序运行时的安全性,使用深拷贝构造函数在一定程度上可以保护数据成员,但有时却不必要,正如上文提到的我们在拷贝完成之后便不再需要传递来的实参,这时便可以使用移动构造函数,编译器会自动避开浅拷贝的使用,这样子便能够提高性能并且有利于维护。
标准库函数 std::move
这个函数可以强制的将一个左值引用当作右值引用来使用,我们来看示例程序:
void ProcessValue(int& i) { std::cout << "LValue processed: " << i << std::endl; } void ProcessValue(int&& i) { std::cout << "RValue processed: " << i << std::endl; } int main() { int a = 0; ProcessValue(a); ProcessValue(std::move(a)); }
运行结果:
LValue processed: 0 RValue processed: 0
std::move在提高 swap 函数的的性能上非常有帮助,一般来说,swap函数的通用定义如下:
template <class T> swap(T& a, T& b) { T tmp(a); // copy a to tmp a = b; // copy b to a b = tmp; // copy tmp to b }
而使用了move函数之后变成了这样:
template <class T> swap(T& a, T& b) { T tmp(std::move(a)); // move a to tmp a = std::move(b); // move b to a b = std::move(tmp); // move tmp to b }
这样子就可以避免了三次不必要的拷贝操作了
参考链接
文章参考自
其中夹了一些自己的理解,这篇原文还有关于泛型的描述,没有学到就不再说了。