程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> C語言 >> C++ >> C++入門知識 >> 表達式左值右值,左值右值

表達式左值右值,左值右值

編輯:C++入門知識

表達式左值右值,左值右值


  左值右值是表達式的屬性,該屬性稱為 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.參考

  • 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

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved