web-dev-qa-db-ja.com

C ++ 11で値渡しは妥当なデフォルトですか?

従来のC++では、値を関数やメソッドに渡すことは、大きなオブジェクトの場合は遅く、一般的に嫌われています。代わりに、C++プログラマーは参照を渡す傾向がありますが、これは高速ですが、所有権、特にメモリ管理(オブジェクトがヒープに割り当てられている場合)に関するあらゆる種類の複雑な質問をもたらします。

現在、C++ 11には、Rvalue参照と移動コンストラクターがあります。つまり、値を関数に渡したり、関数から渡したりするのに安価なラージオブジェクト(std::vectorなど)を実装することができます。

それで、これは、デフォルトでstd::vectorstd::stringなどの型のインスタンスに値渡しすることを意味するのでしょうか?カスタムオブジェクトはどうですか?新しいベストプラクティスは何ですか?

135
Derek Thurn

それは妥当なデフォルトですif本文内にコピーを作成する必要があります。これがデイブ・エイブラハムスです 提唱しています

ガイドライン:関数の引数をコピーしないでください。代わりに、値で渡し、コンパイラにコピーを実行させます。

コードでは、これはこれをしないことを意味します。

_void foo(T const& t)
{
    auto copy = t;
    // ...
}
_

しかし、これを行う:

_void foo(T t)
{
    // ...
}
_

これには、呼び出し元がfooを次のように使用できるという利点があります。

_T lval;
foo(lval); // copy from lvalue
foo(T {}); // (potential) move from prvalue
foo(std::move(lval)); // (potential) move from xvalue
_

最小限の作業のみが行われます。参照で同じことを行うには、void foo(T const&);void foo(T&&);の2つのオーバーロードが必要です。

それを念頭に置いて、私は今、自分の大切なコンストラクタを次のように書きました:

_class T {
    U u;
    V v;
public:
    T(U u, V v)
        : u(std::move(u))
        , v(std::move(v))
    {}
};
_

それ以外の場合、constへの参照渡しは依然として妥当です。

135
Luc Danton

ほとんどすべての場合、セマンティクスは次のいずれかになります。

bar(foo f); // want to obtain a copy of f
bar(const foo& f); // want to read f
bar(foo& f); // want to modify f

他のすべての署名は控えめに、正当な理由がある場合にのみ使用する必要があります。コンパイラは現在、これらを最も効率的な方法でほとんど常に処理します。コードを書くだけです!

71
Ayjay

関数本体内でオブジェクトのコピーが必要な場合、またはオブジェクトの移動のみが必要な場合は、値でパラメーターを渡します。 const&オブジェクトへの非変更アクセスのみが必要な場合。

オブジェクトコピーの例:

void copy_antipattern(T const& t) { // (Don't do this.)
    auto copy = t;
    t.some_mutating_function();
}

void copy_pattern(T t) { // (Do this instead.)
    t.some_mutating_function();
}

オブジェクト移動の例:

std::vector<T> v; 

void move_antipattern(T const& t) {
    v.Push_back(t); 
}

void move_pattern(T t) {
    v.Push_back(std::move(t)); 
}

非変更アクセスの例:

void read_pattern(T const& t) {
    t.some_const_function();
}

根拠については、これらのブログ投稿 Dave Abrahams および Xiang Fan を参照してください。

10
Edward Brey

関数のシグネチャは、その使用目的を反映する必要があります。読みやすさは、オプティマイザーにとっても重要です。

これは、オプティマイザーが最速のコードを作成するための最良の前提条件です-少なくとも理論的には、実際にはそうでなくても、数年後には現実に。

パラメーターの受け渡しのコンテキストでは、パフォーマンスの考慮事項が頻繁に過大評価されます。完全転送はその一例です。 emplace_backのような関数は、ほとんどの場合非常に短く、とにかくインライン化されます。

0