149

関数で a を返すときにコピーされるデータの量とstd::vector、 std::vector をフリーストア (ヒープ上) に配置し、代わりにポインターを返す場合の最適化の大きさは次のとおりです。

std::vector *f()
{
  std::vector *result = new std::vector();
  /*
    Insert elements into result
  */
  return result;
} 

より効率的:

std::vector f()
{
  std::vector result;
  /*
    Insert elements into result
  */
  return result;
} 

?

4

8 に答える 8

201

C++11 では、これが推奨される方法です。

std::vector<X> f();

つまり、値渡しです。

C++11 ではstd::vector移動セマンティクスがあります。これは、関数で宣言されたローカルベクトルが戻り時に移動され、場合によっては、移動がコンパイラによって省略される可能性があることを意味します。

于 2013-03-29T13:48:50.170 に答える
89

値で返す必要があります。

標準には、値による返品の効率を向上させる特定の機能があります。これは「コピー省略」と呼ばれ、より具体的には、この場合は「名前付き戻り値の最適化 (NRVO)」と呼ばれます。

コンパイラはそれを実装する必要はありませんが、コンパイラは関数のインライン化を実装する (または最適化をまったく実行する) 必要はありません。しかし、コンパイラが最適化しない場合、標準ライブラリのパフォーマンスはかなり低下する可能性があり、すべての本格的なコンパイラはインライン化と NRVO (およびその他の最適化) を実装しています。

NRVO が適用されると、次のコードではコピーが行われません。

std::vector<int> f() {
    std::vector<int> result;
    ... populate the vector ...
    return result;
}

std::vector<int> myvec = f();

しかし、ユーザーはこれをしたいかもしれません:

std::vector<int> myvec;
... some time later ...
myvec = f();

初期化ではなく代入であるため、コピー省略はここでのコピーを妨げません。ただし、値で返す必要があります。C++11 では、代入は「移動セマンティクス」と呼ばれる別のものによって最適化されます。C++03 では、上記のコードはコピーを引き起こします。理論的にはオプティマイザーでコピーを回避できるかもしれませんが、実際には難しすぎます。の代わりにmyvec = f()、C++03 では次のように記述します。

std::vector<int> myvec;
... some time later ...
f().swap(myvec);

ユーザーにより柔軟なインターフェースを提供する別のオプションがあります。

template <typename OutputIterator> void f(OutputIterator it) {
    ... write elements to the iterator like this ...
    *it++ = 0;
    *it++ = 1;
}

さらに、既存のベクターベースのインターフェースをサポートすることもできます。

std::vector<int> f() {
    std::vector<int> result;
    f(std::back_inserter(result));
    return result;
}

これは、既存のコードが前もって固定量よりも複雑な方法で使用している場合、既存のコードよりも効率が悪い可能性があります。reserve()ただし、既存のコードが基本的push_backにベクトルを繰り返し呼び出す場合は、このテンプレート ベースのコードも同様に優れているはずです。

于 2013-03-29T14:15:49.890 に答える
3

RVOについての回答を投稿する時が来ました 、私も...

オブジェクトを値で返す場合、関数内で一時オブジェクトとして構築してからコピーするのは不要であるため、コンパイラはこれを最適化し、2 回構築されないようにすることがよくあります。これは戻り値の最適化と呼ばれます。作成されたオブジェクトはコピーされる代わりに移動されます。

于 2013-03-29T13:51:02.430 に答える
2

コンパイラが名前付き戻り値の最適化 ( http://msdn.microsoft.com/en-us/library/ms364057(v=vs.80).aspx ) をサポートしている場合は、ベクターを直接返すことができます。

  1. さまざまな名前のオブジェクトを返すさまざまなパス
  2. EH 状態が導入された複数のリターン パス (すべてのパスで同じ名前のオブジェクトが返される場合でも)。
  3. 返される名前付きオブジェクトは、インライン asm ブロックで参照されます。

NRVO は冗長なコピー コンストラクターとデストラクタの呼び出しを最適化し、全体的なパフォーマンスを向上させます。

あなたの例には実際の違いはありません。

于 2013-03-29T13:53:48.330 に答える
2

C++11 以前の一般的なイディオムは、塗りつぶされているオブジェクトへの参照を渡すことです。

その場合、ベクトルのコピーはありません。

void f( std::vector & result )
{
  /*
    Insert elements into result
  */
} 
于 2013-03-29T13:50:37.300 に答える