2016-08-12 101 views
15

我想知道在函數中傳遞r值時的C++行爲。函數中的r值參數

看看這個簡單的代碼:

#include <string> 

void foo(std::string&& str) { 
    // Accept a rvalue of str 
} 

void bar(std::string&& str) { 
    // foo(str);   // Does not compile. Compiler says cannot bind lvalue into rvalue. 
    foo(std::move(str)); // It feels like a re-casting into a r-value? 
} 

int main(int argc, char *argv[]) { 
    bar(std::string("c++_rvalue")); 
    return 0; 
} 

我知道當我在裏面bar功能我需要使用move功能,以調用foo功能。我現在的問題是爲什麼?

當我的bar函數內部的變量str應該已經是一個r值,但是編譯器就像是一個升價值

有人可以引用一些關於這種行爲的標準嗎? 謝謝!

+5

命名對象始終是左值。這就是爲什麼你需要'移動'。 –

回答

10

str是一個右參考,即它僅供參考rvalues。但它仍然是一個參考,這是一個左翼。您可以使用str作爲變量,這也意味着它是一個左值,而不是臨時右值。

一種左值,根據§3.10.1.1:

一種左值(所謂的,歷史上,因爲左值可以出現在賦值表達式的左側)表示一函數或對象。[實施例如果ë是指針類型的表達式,然後* E是一個左值表達式參考對象或函數,其È。作爲另一個例子,調用返回類型是左值引用的函數的結果是左值。 - 端示例]

而一個右值,根據§3.10.1.4:

一種右值(所謂的,歷史上,因爲右值可能出現在右表達式)是一個xvalue,臨時對象(12.2)或其子對象,或一個與對象無關的值

在此基礎上,str不是暫時對象,並將其是與對象(與對象稱爲str)相關聯,並且因此它不是一個rvalue。

左值的例子使用了一個指針,但它對於引用是同樣的事情,對於右值引用(它們只是一種特殊類型的引用)自然也是如此。

所以,在你的榜樣,str左值,所以你必須std::move它調用foo(只接受右值,不是左值)。

7

在「右值引用」的「右值」指的是種值的基準可以結合

  • rvalue引用可以綁定到右值
    • 左值的引用可以綁定到左值
    • (+多一點)

    這就是它的全部。重要的是,不是是指當你參考時使用得到的值。一旦你有一個引用變量(任何類型的引用!),命名該變量的id表達式總是一個左值。 Rvalues只能作爲臨時值,或者作爲函數調用表達式的值,或者作爲演員表達式的值,或者由於衰減或this而發生。

    這裏有一定的類比解引用指針:解引用指針始終是一個左值,無論是如何獲得這個指針:*p*(p + 1)*f()都是左值。無論你如何來到這件事都無所謂,一旦你有了它,這是一個左翼。

    退一步,也許所有這一切中最有趣的方面是右值引用是一種將右值轉換爲左值的機制。在C++ 11之前沒有產生可變左值的機制。左值到右值的轉換從一開始就一直是該語言的一部分,但發現需要進行右值到左值的轉換花了很長時間。

    3

    我現在的問題是爲什麼?

    我添加了另一個答案,因爲我想強調「爲什麼」的答案。

    即使名爲右值引用可以綁定到右值,它們在使用時被視爲左值。例如:

    struct A {}; 
    
    void h(const A&); 
    void h(A&&); 
    
    void g(const A&); 
    void g(A&&); 
    
    void f(A&& a) 
    { 
        g(a); // calls g(const A&) 
        h(a); // calls h(const A&) 
    } 
    

    雖然右值可結合的f()a參數,一旦結合,a現在被視爲一個左值。特別是,對超載函數g()h()的調用解析爲const A&(左值)重載。 治療a作爲右值內f會導致易錯碼:首先g()「移動版本」將被調用,這將可能竊取a,然後偷a將被髮送到的h()移動過載。

    Reference