パラメータとしての右辺値参照の正しい使用法

あなたの特定のケースでは、2 番目のオーバーロードは役に立ちません。

Load のオーバーロードが 1 つだけある元のコードを使用 、この関数は左辺値と右辺値に対して呼び出されます。

新しいコードでは、最初のオーバーロードが左辺値に対して呼び出され、2 番目が右辺値に対して呼び出されます。ただし、2 番目のオーバーロードは最初のオーバーロードを呼び出します。最後に、どちらかを呼び出すことの効果は、同じ操作 (最初のオーバーロードが行うものは何でも) が実行されることを意味します。

したがって、元のコードと新しいコードの効果は同じですが、最初のコードの方が単純です。

関数が値、左辺値参照、または右辺値参照のいずれによって引数を取る必要があるかの決定は、それが何をするかに大きく依存します。渡された引数を移動する場合は、右辺値参照を取るオーバーロードを提供する必要があります。ムーブ セマンチンについては参考文献がいくつかあるので、ここでは説明しません。

ボーナス :

私の主張を理解するために、この単純な probe を検討してください。 クラス:

struct probe {
    probe(const char*  ) { std::cout << "ctr " << std::endl; }
    probe(const probe& ) { std::cout << "copy" << std::endl; }
    probe(probe&&      ) { std::cout << "move" << std::endl; }
};

この関数を考えてみましょう:

void f(const probe& p) {
    probe q(p);
    // use q;
}

f("foo"); を呼び出す 次の出力が生成されます:

ctr
copy

ここで驚きはありません:一時的な probe を作成します const char* を渡す "foo" .したがって、最初の出力行です。次に、このテンポラリは p にバインドされます と q のコピー pf 内に作成されます .したがって、2 番目の出力行です。

ここで、p を取ることを検討してください 値による、つまり f の変更 へ:

void f(probe p) {
    // use p;
}

f("foo"); の出力 今

ctr

この場合、コピーがないことに驚く人もいるでしょう。一般に、参照によって引数を取得し、それを関数内にコピーする場合は、値によって引数を取得することをお勧めします。この場合、一時変数を作成してコピーする代わりに、コンパイラは引数 (p この場合) 入力から直接 ("foo" )。詳細については、「速度が必要ですか?」を参照してください。値渡し。デイブ・エイブラハムズ著。

このガイドラインには、コンストラクターと代入演算子の 2 つの注目すべき例外があります。

このクラスを検討してください:

struct foo {
    probe p;
    foo(const probe& q) : p(q) { }
};

コンストラクターは probe を受け取ります const 参照によって、それを p にコピーします .この場合、上記のガイドラインに従ってもパフォーマンスは向上せず、probe のコピーコンストラクターはとにかく呼び出されます。ただし、 q を取る 値渡しは、これから取り上げる代入演算子の場合と同様のオーバーロード解決の問題を引き起こす可能性があります。

クラスが probe であるとします。 スローしない swap があります 方法。次に、代入演算子の推奨される実装 (当面は C++03 用語で考えます) は次のとおりです。

probe& operator =(const probe& other) {
    probe tmp(other);
    swap(tmp);
    return *this;
}

では、上記のガイドラインに従って、このように書くとよいでしょう

probe& operator =(probe tmp) {
    swap(tmp);
    return *this;
}

ここで、右辺値参照とムーブ セマンティクスを使用して C++11 に入ります。移動代入演算子を追加することにしました:

probe& operator =(probe&&);

一時的に代入演算子を呼び出すと、両方のオーバーロードが実行可能であり、どちらも優先されないため、あいまいさが生じます。この問題を解決するには、代入演算子の元の実装を使用します (const 参照によって引数を取ります)。

実際、この問題はコンストラクターと代入演算子に固有のものではなく、どの関数でも発生する可能性があります。 (ただし、コンストラクターと代入演算子でそれを経験する可能性が高くなります。) たとえば、 g("foo"); を呼び出します。 g の場合 次の 2 つのオーバーロードがあると、あいまいさが生じます:

void g(probe);
void g(probe&&);

Load の左辺値参照バージョンを呼び出す以外のことをしていない限り 、右辺値が const 左辺値参照にバインドされるため、2 番目の関数は必要ありません。


おそらくそうではありません... Load() 内で何かトリッキーなことをする必要がない限り const 以外のパラメーターが必要です。たとえば、std::move(Path) にしたいかもしれません 別のスレッドに。その場合、移動セマンティクスを使用するのが理にかなっています。

いいえ、逆にする必要があります:

void Asset::load( const std::string& path )
{
     auto path_copy = path;
     load(std::move(path_copy)); // call the below method
}
void Asset::load( std::string&& path )
{
    // complicated method....
}