左值右值是表達式的屬性,該屬性稱為 value category。按該屬性分類,每一個表達式屬於下列之一:
lvalue
left value,傳統意義上的左值
xvalue
expiring value, x值,指通過“右值引用”產生的對象
prvalue
pure rvalue,純右值,傳統意義上的右值(?)
而 xvalue 和其他兩個類型分別復合,構成:
lvalue + xvalue = glvalue
general lvalue,泛左值
xvalue + prvalue = rvalue
右值
++x 與 x++ 假定x的定義為 int x=0;,那麼前者是 lvalue,後者是rvalue。前者修改自身值,並返回自身;後者先創建一個臨時對像,為其賦值,而後修改x的值,最後返回臨時對像。區分表達式的左右值屬性有一個簡便方法:若可對表達式用 & 符取址,則為左值,否則為右值。比如
&obj , &*ptr , &ptr[index] , &++x
有效
&1729 , &(x + y) , &std::string("meow"), &x++
無效
對於函數調用,根絕返回值類型不同,可以是lvalue、xvalue、prvalue:
The result of calling a function whose return type is an lvalue reference is an lvalue
The result of calling a function whose return type is an rvalue reference is an xvalue.
The result of calling a function whose return type is not a reference is a prvalue.
左值和右值表達式都可以是const或non-const。比如,變量和函數的定義為:
1 string one("lvalue"); 2 const string two("clvalue"); 3 string three() { return "rvalue"; } 4 const string four() { return "crvalue"; } View Code那麼表達式:
表達式
分類
one
modifiable lvalue
two
const lvalue
three()
modifiable rvalue
four()
const rvalue
引用
Type&
只能綁定到可修改的左值表達式
const Type&
可以綁定到任何表達式
Type&&
可綁定到可修改的左值或右值表達式
const Type&&
可以綁定到任何表達式
結果:
func(string& s): lvalue func(const string& s): clvalue func(string&& s): rvalue func(const string&& s): crvalue
如果只保留const string& 和 string&& 兩個重載函數,結果為:
func(const string& s): lvalue func(const string& s): clvalue func(string&& s): rvalue func(const string& s): crvalue
C++0x第5章的第6段:
Named rvalue references are treated as lvalues and unnamed rvalue references to objects are treated as xvalues; rvalue references to functions are treated as lvalues whether named or not.
結果
F1(const int&) 1 F1(const int&) 1 F1(const int&) 2 F1(int&&) 2
在這之前,如果寫一個交換兩個值的swap函數:
1 template <class T> swap(T& a, T& b) 2 { 3 T tmp(a); // now we have two copies of a 4 a = b; // now we have two copies of b 5 b = tmp; // now we have two copies of tmp 6 } View Code之後
1 template <class T> swap(T& a, T& b) 2 { 3 T tmp(std::move(a)); 4 a = std::move(b); 5 b = std::move(tmp); 6 } View Codestd::move 接受左值或右值參數,並返回一個右值(其所作工作很簡單)
1 template <class T> 2 typename remove_reference<T>::type&& 3 move(T&& a) 4 { 5 return a; 6 } View Code要是的swap真正發揮作用,需要重載:
1 class T 2 { 3 public: 4 T(T&& ); 5 T& operator = (T&& ); 6 ... View Code為了對比左值引用和右值引用,一開始誤打誤撞,寫了這樣一個函數
1 template <typename Type> void Swap(Type&& sb1, Type&& sb2) 2 { 3 Type sb(sb1); 4 sb1 = sb2; 5 sb2 = sb; 6 } View Code然後
1 int main() 2 { 3 int a=1, b=2; 4 Swap(a, b); 5 std::cout<<a<<" "<<b<<std::endl; 6 return 0; 7 } View Code結果卻是
2 2
不用整數,換用一個自定義的類型試試看:
1 class A 2 { 3 public: 4 A() { 5 std::cout << "Default constructor." << std::endl; 6 m_p = NULL; 7 } 8 9 ~A() { 10 std::cout << "Destructor." << std::endl; 11 delete m_p; 12 } 13 14 explicit A(const int n) { 15 std::cout << "Unary constructor." << std::endl; 16 m_p = new int(n); 17 } 18 19 A(const A& other) { 20 std::cout << "Copy constructor." << std::endl; 21 if (other.m_p) { 22 m_p = new int(*other.m_p); 23 } else { 24 m_p = NULL; 25 } 26 } 27 28 A(A&& other) { 29 std::cout << "Move constructor." << std::endl; 30 m_p = other.m_p; 31 other.m_p = NULL; 32 } 33 34 A& operator=(const A& other) { 35 std::cout << "Copy assignment operator." << std::endl; 36 if (this != &other) { 37 delete m_p; 38 if (other.m_p) { 39 m_p = new int(*other.m_p); 40 } else { 41 m_p = NULL; 42 } 43 } 44 return *this; 45 } 46 47 A& operator=(A&& other) { 48 std::cout << "Move assignment operator." << std::endl; 49 if (this != &other) { 50 delete m_p; 51 m_p = other.m_p; 52 other.m_p = NULL; 53 } 54 return *this; 55 } 56 57 int get() const { 58 return m_p ? *m_p : 0; 59 } 60 61 private: 62 int * m_p; 63 }; 64 65 int main() 66 { 67 A a(1); 68 A b(2); 69 Swap2(a, b); 70 std::cout<<a.get()<<" "<<b.get()<<std::endl; 71 return 0; 72 } View Code結果
Unary constructor. Unary constructor. Copy assignment operator. Copy assignment operator. 2 2 Destructor. Destructor.
只出現了兩個對象,那麼Swap中的臨時對象去哪兒了?
If P is a cv-qualified type, the top level cv-qualifiers of P’s type are ignored for type deduction. If P is a reference type, the type referred to by P is used for type deduction. If P is an rvalue reference to a cv unqualified template parameter and the argument is an lvalue, the type “lvalue reference to A” is used in place of A for type deduction.1 template <class T> int f(T&&); 2 template <class T> int g(const T&&); 3 int i; 4 int n1 = f(i); // calls f<int&>(int&) 5 int n2 = f(0); // calls f<int>(int&&) 6 int n3 = g(i); // error: would call g<int>(const int&&), which 7 // would bind an rvalue reference to an lvalue View Code
也就是前面提到的
1 template <typename Type> void Swap(Type&& sb1, Type&& sb2)
參數推導後
1 void Swap<int&>(int& sb1, int& sb1)
http://blog.csdn.net/zwvista/article/details/5459774
http://blog.csdn.net/hikaliv/article/details/4541429
http://topic.csdn.net/u/20090706/16/514af7e1-ad20-4ea3-bdf0-bfe6d34d9814.html
http://www.artima.com/cppsource/rvalue.html