簡體   English   中英

將局部變量移動到不同類型的返回值

[英]Moving local variable into return value of different type

當從C ++中的函數返回值時,我們有copy elision和(Named)返回值優化,幫助我們創建更高效​​的代碼。 簡而言之,以下代碼:

std::vector<int> make_vec_1(){
    std::vector<int> v;
    v.resize(1e6);
    return v;
}

導致靜默移動或直接構造到返回值的目的地,而不是副本。 圍繞此規則也意味着在返回時顯式移動返回的對象實際上阻止了這些優化。

std::vector<int> make_vec_2(){
    std::vector<int> v;
    v.resize(1e6);
    return std::move(v); // BAD
}

該版本可以防止RVO,如Scott Meyers的Effective Modern C ++ ,第25項中所述。


我的問題是當返回類型不同時會發生什么,但可以從一個或多個局部變量移動構造? 考慮以下函數,每個函數返回一個可選向量:

std::optional<std::vector<int>> make_opt_vec_1(){
    std::vector<int> v;
    v.resize(1e6);
    return v; // no move
}

std::optional<std::vector<int>> make_opt_vec_2(){
    std::vector<int> v;
    v.resize(1e6);
    return std::move(v); // move
}

哪一項是正確的? 線路return std::move(v)起初看起來像是一面紅旗,但我也懷疑這是正確的做法。 返回一對向量的以下兩個函數也是如此:

std::pair<std::vector<int>, std::vector<int>> make_vec_pair_1(){
    std::vector<int> v1, v2;
    v1.resize(1e6);
    v2.resize(1e6);
    return {v1, v2}; // no move
}

std::pair<std::vector<int>, std::vector<int>> make_vec_pair_2(){
    std::vector<int> v1, v2;
    v1.resize(1e6);
    v2.resize(1e6);
    return {std::move(v1), std::move(v2)}; // move
}

在這種情況下,盡管乍一看看起來很奇怪,但我認為進入返回值是更好的事情。

我是否正確,當類型不同時,最好進入返回值,但返回值可以從移動的局部變量構造移動? 我是否誤解了NRVO,或者是否還有其他一些優勢在我這里領先?

我是否正確,當類型不同時,最好進入返回值,但返回值可以從移動的局部變量構造移動? 我是否誤解了NRVO,或者是否還有其他一些優勢在我這里領先?

你確實錯過了一件事。 即使類型不同,也會自動完成隱式移動。

[class.copy.elision] (強調我的)

3在以下復制初始化上下文中,可能會使用移動操作而不是復制操作:

  • 如果return語句中的表達式是一個(可能帶括號的)id-expression,它指定一個對象,該對象具有在最內層封閉函數或lambda表達式的body或parameter-declaration-clause中聲明的自動存儲持續時間 ,或者

  • 如果throw-expression的操作數是非易失性自動對象的名稱(函數或catch子句參數除外),其范圍不會超出最內層封閉try-block的末尾(如果有的話) ,

首先執行重載決策以選擇副本的構造函數,就好像該對象是由rvalue指定的一樣 如果第一個重載決策失敗或未執行,或者所選構造函數的第一個參數的類型不是對象類型的rvalue引用(可能是cv-qualified),則再次執行重載決策,將對象視為左值。 [注意:無論是否發生復制省略,都必須執行此兩階段重載決策。 如果未執行elision,它將確定要調用的構造函數,並且即使調用被省略,也必須可以訪問所選的構造函數。 - 結束說明]

這不取決於類型匹配,並且在完全(N)RVO未發生的情況下是回退行為。 因此,您可以通過在make_opt_vec_2顯式移動來獲得任何make_opt_vec_2

鑒於std::move要么是悲觀還是完全多余,我認為最好不要在返回函數本地對象時這樣做。

您要顯式編寫移動的唯一情況是,返回的表達式更復雜。 在這種情況下,你確實靠自己,而不是移動是一種潛在的悲觀情緒。 所以在make_vec_pair_2 ,進入該對正確的做法。

這里的經驗法則是不僅僅移動作為函數本地對象的id表達式。 否則,離開。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM