天天看點

Lvalues and Rvalues

轉自http://www.cnblogs.com/areliang/archive/2011/11/16/2251480.html

今天看C++模闆的資料,裡面說到lvalue,rvalue的問題,這個問題以前也看到過,也查過相關資料,但是沒有考慮得很深,隻知道rvalue不能取位址,不能指派等等一些規則。今天則突然有了更深層次的了解(也可以說是頓悟,耗時不過幾秒鐘),記錄下來。

下面是我對這兩個單詞字面的意思的猜測:

  • lvalue估計來源于left value。 在指派語句中lvalue = rvalue;位置處于左邊。就是可以修改的值。
  • rvalue估計來源于right value。處于指派語句右邊,是隻讀的不可修改的值。

接下來是我所悟到内容的詳細分析

  • lvalue(左值)是可以指派的,說明它是一個變量,它在記憶體中一定存在,一定有位址。是以&lvalue是有效的,能取到在記憶體中的位址。

    通路lvalue一定會導緻CPU通路存儲器(相對較慢的操作)。

    lvalue的例子:

    1. int a;  
    2. a = 10; // a是lvalue。   
    3. int* p = &a; // &a是rvalue。   
    4. &a = 0; //錯誤,&a不是lvalue,因為a的位址一旦配置設定好了,就不能改變了。  
  • rvalue是不可以指派的,它不是一個變量,在記憶體中沒有存在,沒有位址。它要麼是存在于CPU的寄存器中,要麼是存在于指令中(立即數)。是以隻要對rvalue取位址,那麼就一定是錯誤的(編譯器會抱怨的)。

    通路rvalue不會導緻CPU通路存儲器(對立即數和寄存器的通路很快)。

    rvalue的例子:

    1. int a;  
    2. a = 10; // 10是rvalue,它沒有位址,&10就是錯誤的表達式。從彙編語言的角度來看,10是直接存在于MOV指令中的立即數。   
    3. 10 = a; // 錯誤,10是rvalue,不可指派。   
    4. //函數傳回值屬于rvalue,因為傳回值通常用CPU寄存器傳遞,沒有位址。   
    5. int foo()  
    6. {  
    7.     return 0;  
    8. }  
    9. int b = foo(); //沒問題,函數傳回值是rvalue。   
    10. int* p = &foo(); //錯誤,rvalue沒有位址。   
    11. void bar(int& i)  // 右值是臨時對象,是const類型,不能将非const 類型對象綁定到const對象上
    12. //反之可以,可以将const對象綁定到非const對象上
    13. {  
    14. }  
    15. bar(foo()); //錯誤,bar函數參數需要的是lvalue。  
  • 函數的傳回值是rvalue,對于傳回int, char 等這樣最基本的類型,是通過CPU寄存器傳回的,是以傳回值沒有位址是可以了解的。但是如果函數傳回的是一個使用者自定義類型的對象,肯定不可能通過寄存器來傳回這個對象值的(寄存器大小數量都有限,對象的大小可以非常大),那究竟是怎樣傳回對象的呢?
    1. class UDT  
    2. {  
    3.   int data[100];  
    4. public:  
    5.   UDT()  
    6.   {  
    7.     printf("construct/n");  
    8.   }  
    9.   BBB& operator = (BBB& )  
    10.   {  
    11.     printf("operator =/n");  
    12.     return *this;  
    13.   }  
    14. };  
    15. UDT foo()  
    16. {  
    17.   return UDT();  
    18. }  
    19. void main()  
    20. {  
    21.   UDT obj = foo();  
    22. }  
    23. //輸出:   
    24. construct  
    帶着疑問,我查了查vc編譯出來的代碼,原來obj這個局部變量的位址被壓入了堆棧,foo函數内部以堆棧上的obj位址作為this指針調用了UDT的構造函數。噢,難怪執行UDT obj = foo();這個語句隻有調用了一次構造函數,而沒有調用operator =,這都是因為函數傳回值必須是rvalue這個規則所帶來的好處,如果傳回值是一個lvalue(左值),那麼這個語句一定會調用operator = 運算符。