来看一个示例:
#define func(x) __func(x, "func(" #x ")") void __func(int &x, const char *str) { cout << str << " is left value" << endl; return; } void __func(int &&x, const char *str) { cout << str << " is right value" << endl; return; } int main() { int x = 1234, y = 456; func(1234); func(x); func(x + y); func(x++); func(++x); func(x + 123); func(x *= 2); func(y += 3); func(y * 3); return 0; }
程序输出:
func(1234) is right value func(x) is left value func(x + y) is right value func(x++) is right value func(++x) is left value func(x + 123) is right value func(x *= 2) is left value func(y += 3) is left value func(y * 3) is right value
一个简单的判断左值还是右值的方法:到了下一行还能不能通过单一变量访问到该值。能便是左值,否则是右值,其中字面量一定是右值。
比如func(x)
中x
的值在下一行可以访问到,那么x
就是左值
func(x + y)
中x + y
的值在下一行访问不到,那么x + y
就是右值
其他同理。
再来看一组示例:
#define func(x) __func(x, "func(" #x ")") #define func2(x) __func2(x, "func2(" #x ")") void __func2(int &x, const char *str) { cout << str << " is left value" << endl; return; } void __func2(int &&x, const char *str) { cout << str << " is right value" << endl; return; } void __func(int &x, const char *str) { cout << str << " is left value" << endl; func2(x); return; } void __func(int &&x, const char *str) { cout << str << " is right value" << endl; func2(x); return; }
在fun
函数中调用func2
函数,程序输出:
func(1234) is right value func2(x) is left value func(x) is left value func2(x) is left value func(x + y) is right value func2(x) is left value func(x++) is right value func2(x) is left value func(++x) is left value func2(x) is left value func(x + 123) is right value func2(x) is left value func(x *= 2) is left value func2(x) is left value func(y += 3) is left value func2(x) is left value func(y * 3) is right value func2(x) is left value
可以发现,调用func2
函数都是以左值的方式调用的,即使原来的值是右值。
比如func(y * 3)
调用是右值对应的函数,而在func2
中却调用了左值对应的函数。如果我们仍然想以右值的特性向下传递,可以利用move语义,让他以右值的属性向下传递
void __func(int &&x, const char *str) { cout << str << " is right value" << endl; //向下传递右值属性 func2(move(x)); return; }
这时输出:
func(1234) is right value func2(move(x)) is right value func(x) is left value func2(x) is left value func(x + y) is right value func2(move(x)) is right value func(x++) is right value func2(move(x)) is right value func(++x) is left value func2(x) is left value func(x + 123) is right value func2(move(x)) is right value func(x *= 2) is left value func2(x) is left value func(y += 3) is left value func2(x) is left value func(y * 3) is right value func2(move(x)) is right value
还可以使用forward
向下传递右值属性
void __func(int &&x, const char *str) { cout << str << " is right value" << endl; func2(forward<int &&>(x)); return; }
move和forward可以理解是为了调用到正确的重载函数版本。
再来看一下右值引用在面向对象中的应用
class myvector { public: myvector(int n = 10) : __size(n), data(new int[n]) { cout << this << endl; cout << "default constructor" << endl; } //左值引用 myvector(const myvector &v) : __size(v.size()), data(new int[__size]) { cout << this << endl; cout << "deep copy constructor" << endl; for (int i = 0; i < size(); i++) data[i] = v[i]; return; } myvector operator+(const myvector &v) { myvector ret(v.size() + this->size()); myvector &now = *this; for (int i = 0; i < size(); i++) { ret[i] = now[i]; } for (int i = size(); i < ret.size(); i++) { ret[i] = v[i - size()]; } return ret; } int &operator[](int ind) const { return this->data[ind]; } int size() const { return __size; } ~myvector() { cout << this << endl; cout << "destructor" << endl; if (data) { delete[] data; } data = nullptr; } private: int __size; int *data; }; ostream &operator<<(ostream &out, const myvector &v) { for (int i = 0; i < v.size(); i++) { out << v[i] << " "; } return out; } int main() { myvector v1, v2; for (int i = 0; i < v1.size(); i++) v1[i] = rand() % 100; for (int i = 0; i < v2.size(); i++) v2[i] = rand() % 100; myvector v3(v1 + v2); cout << v1 << endl; cout << v2 << endl; cout << v3 << endl; return 0; }
实现一个vector,程序输出:
中间调用了深拷贝,若通过右值引用,实现一个移动拷贝:
//右值引用,移动构造 myvector(myvector &&v) : __size(v.size()), data(v.data) { cout << this << endl; cout << "move copy constructor" << endl; v.data = nullptr;//这里一定要记得 v.__size = 0; }
移动拷贝不用一个个拷贝数据,而是直接拿取临时对象的值
程序输出:
如果我们想直接抢夺一个对象的资源,而不是拷贝,就可以通过move来调用移动构造函数。比如:
int main() { myvector v1, v2; for (int i = 0; i < v1.size(); i++) v1[i] = rand() % 100; for (int i = 0; i < v2.size(); i++) v2[i] = rand() % 100; myvector v3(v1 + v2); cout << "v1: " << v1 << endl; cout << "v2: " << v2 << endl; cout << "v3: " << v3 << endl; myvector v4(move(v1));//v1中的数据给v4,move向下传递右值引用 cout << "v1: " << v1 << endl; cout << "v4: " << v4 << endl; return 0; }
关于引用的绑定顺序:
#include <iostream> using namespace std; void func1(int &x) { cout << __PRETTY_FUNCTION__ << "called" << endl; return; } void func1(const int &x) { cout << __PRETTY_FUNCTION__ << "called" << endl; return; } void func1(int &&x) { cout << __PRETTY_FUNCTION__ << "called" << endl; return; } void func1(const int &&x) { cout << __PRETTY_FUNCTION__ << "called" << endl; return; } int main() { int n; const int y = 123; func1(n);// 优先绑定func1(int &); func1(y);// func1(const int &); func1(123 + 456);// func1(int &&); return 0; }
程序输出:
值得注意的是,const左值引用可以绑定任何值,比如:
#include <iostream> using namespace std; //只有const左值引用时 void func1(const int &x) { cout << __PRETTY_FUNCTION__ << "called" << endl; return; } int main() { int n; const int y = 123; func1(n);// 优先绑定func1(int &); func1(y);// func1(const int &); func1(123 + 456);// func1(int &&); return 0; }
程序输出: