62

多くの場合、関数からローカルを返すときに、RVO (戻り値の最適化) が開始されます。ただし、明示的に使用std::moveすると、RVO が発生しない場合は少なくとも移動が強制されますが、可能な場合は RVO が引き続き適用されると思いました。しかし、そうではないようです。

#include "iostream"

class HeavyWeight
{
public:
    HeavyWeight()
    {
        std::cout << "ctor" << std::endl;
    }

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

    HeavyWeight(HeavyWeight&& other)
    {
        std::cout << "move" << std::endl;
    }
};

HeavyWeight MakeHeavy()
{
    HeavyWeight heavy;
    return heavy;
}

int main()
{
    auto heavy = MakeHeavy();
    return 0;
}

このコードを VC++11 と GCC 4.71、デバッグおよびリリース ( -O2) 構成でテストしました。copy ctor が呼び出されることはありません。move ctor は、デバッグ構成で VC++11 によってのみ呼び出されます。実際、特にこれらのコンパイラではすべて問題ないように見えますが、私の知る限り、RVO はオプションです。

ただし、明示的に使用する場合move:

HeavyWeight MakeHeavy()
{
    HeavyWeight heavy;
    return std::move(heavy);
}

move ctor は常に呼び出されます。したがって、それを「安全」にしようとすると、さらに悪化します。

私の質問は次のとおりです。

  • なぜstd::moveRVO を防ぐのですか?
  • 「最善を期待」してRVOに頼る方が良いのはいつですか?また、いつ明示的に使用する必要がありますstd::moveか? または、言い換えると、RVO が適用されていない場合でも、コンパイラの最適化にその作業を行わせ、移動を強制するにはどうすればよいでしょうか?
4

2 に答える 2

45

コピーと移動の省略が許可されるケースは、標準 (バージョン N3690) のセクション 12.8 §31 にあります。

特定の基準が満たされると、コピー/移動操作用に選択されたコンストラクターおよび/またはオブジェクトのデストラクタに副作用がある場合でも、実装はクラス オブジェクトのコピー/移動構築を省略できます。このような場合、実装は、省略されたコピー/移動操作のソースとターゲットを、同じオブジェクトを参照する 2 つの異なる方法として扱い、そのオブジェクトの破棄は、2 つのオブジェクトが削除されていた時間のうちの遅い方の時点で発生します。最適化なしで破壊されました。コピー省略と呼ばれるこのコピー/移動操作の省略は、次の状況で許可されます (複数のコピーを排除するために組み合わせることができます)。

  • クラスの戻り値の型を持つ関数のreturnステートメントで、式が関数の戻り値の型と同じ cv-unqualified 型を持つ不揮発性自動オブジェクト (関数または catch-clause パラメーター以外) の名前である場合、自動オブジェクトを関数の戻り値に直接構築することにより、コピー/移動操作を省略できます
  • [...]
  • 参照 (12.2) にバインドされていない一時クラス オブジェクトが同じ cv 非修飾型のクラス オブジェクトにコピー/移動される場合、一時オブジェクトをターゲットに直接構築することにより、コピー/移動操作を省略できます。省略されたコピー/移動の
  • [...]

(省略した 2 つのケースは、最適化にとってあまり重要ではないと考える例外オブジェクトのスローとキャッチのケースを指します。)

したがって、return ステートメントでは、式がローカル変数の名前である場合にのみ、コピー省略が発生する可能性があります。と書くstd::move(var)と、それはもう変数の名前ではありません。したがって、標準に準拠する必要がある場合、コンパイラは移動を除外できません。

Stephan T. Lavavej は、 Going Native 2013 (代替ソース)でこれについて話し、状況とここで避けるべき理由を正確に説明しstd::move()ました。38:04分から視聴開始。基本的に、戻り型のローカル変数を返す場合、通常は右辺値として扱われるため、デフォルトで移動が有効になります。

于 2013-10-09T12:28:41.920 に答える
17

RVO が適用されていない場合でも、コンパイラの最適化にその作業を行わせ、移動を強制するにはどうすればよいですか?

このような:

HeavyWeight MakeHeavy()
{
    HeavyWeight heavy;
    return heavy;
}

リターンをムーブに変換することは必須です。

于 2013-10-09T09:04:40.603 に答える