簡體   English   中英

在 RVO 無法完成的情況下,我們是否應該寫 `std::move`?

[英]Should we write `std::move` in the cases when RVO can not be done?

眾所周知, std::move不應該應用於函數返回值,因為它可以防止 RVO(返回值優化)。 如果我們確實知道 RVO 不會發生,我對這個問題很感興趣。

這就是 C++14 標准所說的 [12.8/32]

當滿足復制/移動操作的省略條件但不滿足異常聲明時,並且要復制的對象由左值指定,或者當返回語句中的表達式是(可能是括號)id-使用在最內層封閉函數或 lambda 表達式的主體或參數聲明子句中聲明的自動存儲持續時間命名對象的表達式,首先執行為復制選擇構造函數的重載決策,就好像對象由右值指定一樣. 如果第一個重載決議失敗或未執行,或者如果所選構造函數的第一個參數的類型不是對對象類型的右值引用(可能是 cv 限定的),則再次執行重載決議,將對象視為左值。 [注意:無論是否會發生復制省略,都必須執行此兩階段重載解析。 它確定如果不執行省略則要調用的構造函數,並且即使調用被省略,所選構造函數也必須是可訪問的。 ——尾注]

這是《 Effective Modern C++ 》一書中的解釋

標准支持 RVO 的部分繼續說,如果滿足 RVO 的條件,但編譯器選擇不執行復制省略,則返回的對象必須被視為右值。 實際上,標准要求當 RVO 被允許時,復制省略發生或 std::move 隱式應用於返回的本地對象

據我了解,當返回對象一開始不能被省略時,它應該被視為rvalue 在這些示例中,我們可以看到,當我們傳遞大於5的參數時,對象會被移動,否則會被復制。 這是否意味着當我們知道 RVO 不會發生時,我們應該顯式編寫std::move

#include <iostream>
#include <string>


struct Test
{
    Test() {}

    Test(const Test& other)
    {
        std::cout << "Test(const Test&)" << std::endl;
    }

    Test(Test&& other)
    {
        std::cout << "Test(const Test&&)" << std::endl;
    }
};

Test foo(int param)
{
    Test test1;
    Test test2;
    return param > 5 ? std::move(test1) : test2;
}

int main()
{
    Test res = foo(2);
}

該程序的輸出是Test(const Test&)

您的示例中發生的事情與RVO無關,而與三元operator ?無關operator ? 如果使用if語句重寫示例代碼,程序的行為將是預期的行為。 foo定義更改為:

Test foo(int param)
  {
  Test test1;
  Test test2;
  if (param > 5)
    return std::move(test2);
  else
    return test1;
  }

將輸出Test(Test&&)


如果你寫(param>5)?std::move(test1):test2會發生什么(param>5)?std::move(test1):test2是:

  1. 三元運算符結果推導為prvalue [expr.cond] / 5
  2. 然后test2通過lvalue-to-rvalue轉換,導致[expr.cond] / 6中所需的復制初始化
  3. 然后將返回值的移動構造省略[class.copy] /31.3

因此,在您的示例代碼中,移動elision發生,然而在形成三元運算符的結果所需的復制初始化之后。

實際上,在您的示例中,發生了復制省略。 如果您使用參數-fno-elide-constructors明確禁止 RVO/NRVO,那么它可能會打印(我使用 Apple clang 版本 13.0.0 和 Homebrew GCC 11.2.0_2)

Test(const Test&)
Test(const Test&&)
Test(const Test&&)

調用第一個復制構造函數以評估表達式param > 5 ? std::move(test1) : test2 param > 5 ? std::move(test1) : test2 由於復制省略不可用,移動構造函數將至少被調用一次。 所以我認為在 return 語句上添加std::move總是多余的。

暫無
暫無

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

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