2013-03-10 60 views
3

當我閱讀一些文章時,右值引用和移動語義通常一起描述。然而,據我所知,右值引用只是引用右值,並且與移動語義無關。移動語義可能會被實現,甚至可能甚至不使用右值引用。所以問題是,爲什麼要移動構造函數/運算符=使用右值引用?只是爲了更容易編寫代碼?爲什麼右值引用與移動語義相關聯?

回答

8

考慮這個問題。我們要支持兩種基本的移動操作:移動「構造」並移動「分配」。我在那裏使用了引號,因爲我們不一定要用構造函數或移動賦值操作符來實現它們;我們可以使用別的東西。

移動「構造」意味着通過從現有對象中傳輸內容來創建新對象,從而刪除舊對象不會釋放現在在新對象中使用的資源。移動「賦值」意味着採用預先存在的對象並從現有對象中傳輸內容,以便刪除舊對象不會釋放現在在新對象中使用的資源。

好的,所以這些是我們想要做的操作。那麼,該怎麼做呢?

移動「建築」。雖然我們沒有來執行這個帶有構造函數調用,但我們真的想要。我們不想強迫人們進行兩階段移動建設,即使它是在一些神奇的功能調用之後。所以我們希望能夠實現運動作爲構造函數。好的。

問題1:構造函數沒有名字。因此,您只能根據參數類型和重載解析來區分它們。我們知道T類型的對象的移動構造函數必須將T類型的對象作爲參數。因爲它只需要一個參數,所以它看起來完全像一個拷貝構造函數。好的,現在我們需要一些方法來滿足超載。我們可以引入一些標準庫類型,一個std::move_ref。這將像std::reference_wrapper,但它將是一個獨特的類型。因此,你可以說移動構造函數是一個構造函數,它需要一個std::move_ref<T>。好,很好:問題解決了。

只有不;我們現在有新的問題。考慮下面的代碼:

std::string MakeAString() { return std::string("foo"); } 

std::string data = MakeAString(); 

忽略省音,C++ 11的表情值類別規則指出這是從一個函數由返回值類型是一個prvalue。因此,它會自動被移動構造函數/賦值運算符使用。不需要std::move等。

要做到這一點的方法就是需要這樣的:

std::string MakeAString() { return std::move(std::string("foo")); } 

std::string data = std::move(MakeAString()); 

那些std::move電話將需要避免複製的兩個。您必須移出臨時值並返回返回值,然後移出返回值並返回data(再次忽略elision)。

如果您認爲這只是一個小小的煩惱,請考慮還有哪些右值引用了我們:完美轉發。如果沒有特殊的引用合攏規則,就無法編寫正確的轉發功能,它可以完美地轉發副本並移動語義std::move_ref將是一個真正的C++類型;你不能像使用右值引用一樣將任意規則像參考摺疊一樣放在它上面。

在一天結束時,您需要某種形式的語言結構,而不僅僅是一種庫類型。通過使它成爲一種新的參考,您可以定義新的規則,以便綁定到該參考(以及不可以)的內容。而且您可以定義特殊的引用合攏規則,使完美的轉發成爲可能。

1

連接的是,它是安全的從右值(因爲(在不存在鑄件)右值指的是在其壽命的末尾的對象),所以一個構造函數的移動右值引用可以通過從引用對象中盜取/移動而安全地實現。

從C++ - 的視圖語言點,這是在連接的端部,但在標準庫進一步通過不斷從左值拷貝和建設從右值舉動使施工此連接上膨脹,並通過提供幫助函數(例如std::move),它使得直接選擇是移動還是複製特定對象(通過改變表達式中導致複製/移動的對象的值類別)。

移動語義可以在沒有右值-參考的情況下實現,但它會少得多整齊。許多問題需要解決:

  1. 如何通過非const引用捕捉右值

  2. 如何區分副本的構造函數和移動的構造函數?

  3. 如何確保移動被用在任何他們將會進行安全優化的地方?

  4. 如何編寫可移動和可複製對象的通用代碼?

相關問題