表達式左值右值,左值右值
左值右值是表達式的屬性,該屬性稱為 value category。按該屬性分類,每一個表達式屬於下列之一:
lvalue
left value,傳統意義上的左值
xvalue
expiring value, x值,指通過“右值引用”產生的對象
prvalue
pure rvalue,純右值,傳統意義上的右值(?)
而 xvalue 和其他兩個類型分別復合,構成:
lvalue + xvalue = glvalue
general lvalue,泛左值
xvalue + prvalue = rvalue
右值
1.區分
++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.
2.const vs non-const
左值和右值表達式都可以是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&&
可以綁定到任何表達式
3.重載函數

1 #include <iostream>
2 #include <string>
3 using namespace std;
4
5 string one("lvalue");
6 const string two("clvalue");
7 string three() { return "rvalue"; }
8 const string four() { return "crvalue"; }
9
10 void func(string& s)
11 {
12 cout << "func(string& s): " << s << endl;
13 }
14
15 void func(const string& s)
16 {
17 cout << "func(const string& s): " << s << endl;
18 }
19
20 void func(string&& s)
21 {
22 cout << "func(string&& s): " << s << endl;
23 }
24
25 void func(const string&& s)
26 {
27 cout << "func(const string&& s): " << s << endl;
28 }
29
30 int main()
31 {
32 func(one);
33 func(two);
34 func(three());
35 func(four());
36 return 0;
37 }
View Code
結果:
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
4.右值引用
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.
- 具名右值引用被視為左值
- 無名對對象的右值引用被視為x值
- 對函數的右值引用無論具名與否都將被視為左值

![]()
1 #include <iostream>
2 #include <string>
3
4 void F1(int&& a)
5 {
6 std::cout<<"F1(int&&) "<<a<<std::endl;
7 }
8
9 void F1(const int& a)
10 {
11 std::cout<<"F1(const int&) "<<a<<std::endl;
12 }
13
14 void F2(int&& a)
15 {
16 F1(a);
17 }
18
19 int main()
20 {
21 int && a=1;
22 F2(a);
23 F1(a);
24 F2(2);
25 F1(2);
26 return 0;
27 }
View Code
結果
F1(const int&) 1
F1(const int&) 1
F1(const int&) 2
F1(int&&) 2
5.移動語義
在這之前,如果寫一個交換兩個值的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 Code
std::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
6.模板參數類型
為了對比左值引用和右值引用,一開始誤打誤撞,寫了這樣一個函數

![]()
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中的臨時對象去哪兒了?
C++0x 14.8.2.1
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)
7.參考
-
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