21
struct TestConstRef {
    std::string str;
    Test(const std::string& mStr) : str{mStr} { }
};

struct TestMove {
    std::string str;
    Test(std::string mStr) : str{std::move(mStr)} { }
};

GoingNative 2013 を見た後、シンク引数は常に値で渡し、 で移動する必要があることを理解しましたstd::moveTestMove::ctorこのイディオムを適用する正しい方法はありますか? TestConstRef::ctor より良い/より効率的なケースはありますか?


些細なセッターはどうですか?次のイディオムを使用するか、a を渡す必要がありconst std::string&ますか?

struct TestSetter {
    std::string str;
    void setStr(std::string mStr) { str = std::move(str); }
};
4

2 に答える 2

22

簡単な答えは: はい。


理由も非常に単純です。値で保存する場合、(一時的なものから) 移動するか、(左辺値から) コピーを作成する必要があるかもしれません。両方の方法で、両方の状況で何が起こるかを調べてみましょう。

一時的なものから

  • const-ref で引数を取得すると、一時的なものは const-ref にバインドされ、そこから移動できなくなるため、(役に立たない) コピーを作成することになります。
  • 引数を値で取得する場合、値は一時 (移動) から初期化され、次に自分で引数から移動するため、コピーは作成されません。

std::array<T, N>1 つの制限: 1つではなく 2 つのコピーを実行したため、効率的な移動コンストラクター ( など) のないクラス。

左辺値から(または const 一時的ですが、誰がそれを行うでしょうか...)

  • const-ref で引数を取った場合、そこでは何も起こらず、それをコピーします (そこから移動することはできません)。したがって、単一のコピーが作成されます。
  • 引数を値で取得する場合、それを引数にコピーしてから移動すると、単一のコピーが作成されます。

1 つの制限: 同じ...移動がコピーに似ているクラス。

したがって、簡単な答えは、ほとんどの場合、シンクを使用することで不要なコピーを回避する (移動によってそれらを置き換える) ということです。

唯一の制限は、移動コンストラクターがコピー コンストラクターと同じくらい高価な (またはそれに近い) クラスです。その場合、1 つのコピーではなく 2 つの移動を行うのが「最悪」です。ありがたいことに、そのようなクラスはまれです (配列は 1 つのケースです)。

于 2013-09-07T13:30:30.100 に答える
11

この質問にはすでに受け入れられている回答があるため、少し遅れていますが、とにかく...ここに代替案があります:

struct Test {
    std::string str;
    Test(std::string&& mStr) : str{std::move(mStr)} { } // 1
    Test(const std::string& mStr) : str{mStr} { } // 2
};

なぜそれが良いのでしょうか?次の 2 つのケースを考えてみましょう。

仮から(場合// 1

に対して呼び出される move-constructor は 1 つだけですstr

左辺値から (case // 2)

に対して呼び出されるコピー コンストラクターは 1 つだけですstr

おそらくこれ以上良くなることはありません。

しかし、待ってください。他にもあります。

呼び出し側で追加のコードは生成されません! コピー コンストラクターまたはムーブ コンストラクター (インライン化されているかどうかに関係なく) の呼び出しは、呼び出された関数 (ここでは: Test::Test) の実装に含めることができるため、必要なコードのコピーは 1 つだけです。値によるパラメーターの受け渡しを使用する場合、呼び出し元は、関数に渡されるオブジェクトを生成する責任があります。これは大規模なプロジェクトで発生する可能性があり、可能であれば回避するようにしています。

于 2013-09-07T14:27:14.710 に答える