web-dev-qa-db-ja.com

C ++ 11の関数呼び出しからstd :: vectorを返す適切な方法(セマンティクスを移動)

Std :: vector(または他のいくつかのSTLコンテナー)を埋めたい:

class Foo {
public:
  Foo(int _n, const Bar &_m);
private:
  std::vector<Foo> fooes_;
}

1。見栄えの良い俳優、高価なパフォーマンス

std::vector<Foo> get_vector(int _n, const Bar &_m) {
  std::vector<Foo> ret;
  ... // filling ret depending from arguments
  return ret;
}

Foo::Foo(int _n, const Bar &_m) : fooes_(get_vector(_n, _m) {}

2。パフォーマンスが向上し、見栄えが悪くなる

void fill_vector(int _n, const Bar &_m, std::vector<Foo> &_ret) {
  ... // filling ret depending from arguments
}

Foo::Foo(int _n, const Bar &_m) { fill_vector(_n, _m, fooes_); }

書き換えることは可能ですかget_vector C++ 0xを使用した最初の例の関数(セマンティクス機能の移動など)により、冗長なコピーとコンストラクターの呼び出しを回避しますか?

33
Loom

C++ 0x互換のコンパイラと標準ライブラリを使用している場合は、最初の例何もしないの方がパフォーマンスが向上します。 get_vector(_n, _m)の戻り値は一時的なものであり、std::vectorの移動コンストラクター(右辺値参照をとるコンストラクター)は自動的に呼び出され、ユーザー側でこれ以上作業する必要はありません。

一般に、非ライブラリライターは右辺値参照を直接使用する必要はありません。ある程度のメリットを自動的に獲得できます。

46
John Calsbeek

コンパイラーが名前付き戻り値の最適化を行う限り、(1)と(2)はC++ 0xがなくても同じパフォーマンスであると私は信じています。コピーも行わないでください。 どんな動きも

私が間違っている場合は訂正してください。間違っている場合はNRVOと誤解しています。

16
Clinton

検討している特定のケースでは、最初の実装は2番目の実装と同じくらい効率的です。コンパイラーは、get_vector関数のretのコピーを戻り値に最適化し、move semanticsを使用してベクターの所有権をコンテナークラスに転送します。コンテナ内の要素の数とサイズに関係なく、ベクター内での移動の構築には、3つのポインタのコピーが必要です(実装に依存しますが、適切な概算です)。変更する参照としてベクターを渡すには、単一のポインターコピー(おおよそのコスト)が必要ですが、ベクターに対して実行する操作は、どちらのオプションのコストも支配します。

変更のためにベクトルを関数に渡す方が高速である場合がありますが、これらはまれであり、ベクトル自体よりもドメインに関連しています。単にそれを無視して、最初に保守性のためにコードを記述し、プログラムが遅い場合は、プロファイリングして、プログラムのコストがどこにあるかを判別し、次に最適化について考えてください。興味深い部分は、プロファイルを作成すると、ボトルネックが何であるかをおそらく理解しているということです。つまり、何を変更すべきかについてのヒントが得られるということです。