不破不立

码农一枚

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

  左值右值是表达式的属性,该属性称为 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

  左值右值表达式都可以是constnon-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.参考

posted on 2015-09-17 19:10  jackjoe  阅读(1379)  评论(0编辑  收藏  举报
levels of contents