2010-11-10 46 views
32

今天我一直在看看我在過去幾天一直在努力的一些代碼,並開始閱讀移動語義學,特別是std :: move。我有幾個問題要問你的專業人員,以確保我走的是正確的道路,而不是做出任何愚蠢的假設!這是C++'move'語義的正確用法嗎?

首先:

1)本來,我的代碼有一個返回一大載體功能:

template<class T> class MyObject 
{ 
public: 
    std::vector<T> doSomething() const; 
    { 
     std::vector<T> theVector; 

     // produce/work with a vector right here 

     return(theVector); 
    }; // eo doSomething 
}; // eo class MyObject 

鑑於 「theVector」 是在這個暫時的, 「用完即棄」,我將功能修改爲:

std::vector<T>&& doSomething() const; 
    { 
     std::vector<T> theVector; 

     // produce/work with a vector right here 

     return(static_cast<std::vector<T>&&>(theVector)); 
    }; // eo doSomething 

這是正確的嗎?這樣做的任何陷阱?

2)我注意到在函數中,我返回std::string它自動調用移動構造函數。調試返回字符串(謝謝你,阿拉貢),我注意到它稱爲一個明確的移動構造函數。爲什麼有一個字符串類,而不是矢量?

我沒有做任何修改,該功能利用移動語義的:

// below, no need for std::string&& return value? 
std::string AnyConverter::toString(const boost::any& _val) const 
{ 
    string ret; 
    // convert here 
    return(ret); // No need for static_cast<std::string&&> ? 
}; // eo toString 

3)最後,我想要做一些性能測試,是令人驚訝的快速結果我是因爲std :: move語義,還是我的編譯器(VS2010)做了一些優化?

(省略爲簡潔_getMilliseconds()實施)

std::vector<int> v; 
for(int a(0); a < 1000000; ++a) 
    v.push_back(a); 

std::vector<int> x; 
for(int a(0); a < 1000000; ++a) 
    x.push_back(a); 

    int s1 = _getMilliseconds(); 
std::vector<int> v2 = v; 
    int s2 = _getMilliseconds(); 
std::vector<int> v3 = std::move(x); 
    int s3 = _getMilliseconds(); 

    int result1 = s2 - s1; 
    int result2 = s3 - s2; 

的結果,很明顯,真棒。 result1是一項標準任務,耗時630毫秒。第二個結果是0ms。這是對這些東西的很好的性能測試嗎?

我知道這對很多人來說都是顯而易見的,但我想確保在理解我的代碼之前我明白了語義。

在此先感謝!

+4

爲什麼使用'static_cast '而不是'std :: move'? – GManNickG 2010-11-10 19:49:33

+0

@GMan - 這就是我問這些問題的原因。我的動機最初來自這裏:http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2002/n1377.htm#string無可否認我可能誤讀的動機,因此本文:) – 2010-11-10 19:52:07

+0

如果你真的想學習右值,我已經添加了一個鏈接到我的答案供你閱讀。 – GManNickG 2010-11-10 20:00:30

回答

37

的引用仍是一個參考。以同樣的方式,你不能在C++ 03中返回一個本地引用(或者你得到了UB),你不能在C++ 0x中。你最終會提到一個死對象的引用;它恰好是一個右值引用。因此,這是錯誤的:

std::vector<T>&& doSomething() const 
{ 
    std::vector<T> local; 

    return local; // oops 
    return std::move(local); // also oops 
} 

您應該只是你兩個數字所看到的:

// okay, return by-value 
std::vector<T> doSomething() const 
{ 
    std::vector<T> local; 

    return local; // exactly the same as: 
    return std::move(local); // move-construct value 
} 

變量函數的局部是暫時的,當你回來,所以沒有必要改變你的任何碼。返回類型是負責實現移動語義的東西,而不是你。

你想用std::move明確移動的東西,當它不能正常在測試完成,等等。 (這似乎是好的;是在發佈?你應該輸出矢量的內容,或編譯器將優化它。)

如果你想了解右值引用,read this

+0

測試處於調試模式。 – 2010-11-10 19:53:24

+4

@Moo:然後測試沒用。您必須在Release中進行配置文件,並且最好在實際應用程序中進行配置。在這種情況下確實沒有必要;移動語義只需贏。 – GManNickG 2010-11-10 19:58:18

+4

我似乎記得'return std :: move(local)'可以防止copy-elision,所以我不會認爲它與'return local'相同,但是我不是100%確定的。 – fredoverflow 2010-11-10 20:02:52

5

移動東西的標準方式是std::move(x),而不是static_cast。 AFAIK,命名的返回值優化很可能會用值返回一個向量,所以它在移動語義之前也會執行得很好。

您的性能測試很好地說明了移動語義對性能的好處:複製分配必須複製一百萬個元素,移動分配基本上只是交換向量的內部指針,這是一個不重要的單詞任務或兩個,只是幾個週期。

14
return(theVector); 

這已經隱含移動由於特殊的語言規則,因爲theVector是一個本地對象。見第12節。8和第34和35:

當滿足特定條件時,一種實現被允許省略類 對象的複製/移動結構,即使在複製/移動的構造和/或析構對象有副作用。在這種情況下, 的實現將忽略的複製/移動操作的源和目標視爲簡單地引用同一對象的兩種不同的方式,並且當該兩個對象在時間的後期發生該對象的銷燬沒有優化就會被銷燬。一份,省音/移動 操作,稱爲複製省略,在下列情況下是允許的(可以組合到 消除多個副本):

- return語句中的功能與類返回類型,當表達是 用相同的CV-不合格類型作爲函數返回類型,所述 複製/移動操作可以通過直接構建自動對象到函數的 返回值

可以省略非易失性自動對象的名稱

[...]

當滿足複製操作的刪除條件並且要複製的對象被指定爲左值時,首先執行用於選擇副本的構造函數的重載解析,就好像對象是由右值指定的

請注意,您必須返回std::vector<T>由值),一個std::vector<T>&&參照)。

但爲什麼括號? return不是函數:

return theVector; 
+0

這主要是由於習慣,我一直這樣做。這真的很糟嗎?我有點喜歡它。 for()也不是一個函數;)(不是在這裏開始一場火焰戰爭,那大部分是舌尖) – 2010-11-10 19:58:40

+6

@Moo:是的,但'for' *需要*圓括號。 – fredoverflow 2010-11-10 19:59:27

7

要添加到GMan的答案:即使您將返回類型更改爲std::vector<T>(沒有任何引用,否則您將獲得UB),但在「1)」中返回表達式的更改絕不會使性能更好,但可能會讓它變得更糟。作爲std::vector具有移動構造函數,並返回一個本地對象,vector的拷貝構造函數將被調用,不管你寫return theVector;return static_cast<std::vector<T>&&>(theVector);,或return std::move(theVector)。在最後兩種情況下,編譯器將被迫調用移動構造函數。但是,在第一種情況下,如果它可以爲該功能執行NRVO,則它可以完全優化移動的自由度。如果由於某種原因NRVO不可行,那麼只有編譯器纔會調用移動構造函數。因此,如果x是從函數返回的函數中的本地非靜態對象,請不要將return x;更改爲return std::move(x);,否則將阻止編譯器使用其他優化機會。

+1

「要添加到GMan的答案」,請添加評論,而不是新的答案。 – Shoe 2013-10-08 18:33:09