22

Linux と Windows の両方でアプリを作成していますが、GCC ビルドがコピー コンストラクターへの無駄な呼び出しを大量に生成していることに気付きました。

この動作を生成するコードの例を次に示します。

struct A
{
    A()                { std::cout << "default" << std::endl; }
    A(A&& rvalue)      { std::cout << "move"    << std::endl; }
    A(const A& lvalue) { std::cout << "copy"    << std::endl; }
    A& operator =(A a) { std::cout << "assign"  << std::endl; return *this; }
};

BOOST_AUTO_TEST_CASE(test_copy_semantics)
{
    std::vector<A> vec_a( 3 );
}

このテストは、3 つの要素のベクトルを作成するだけです。左辺値がないため、3 つのデフォルト コンストラクター呼び出しと 0 コピーが必要Aです。

Visual C++ 2010 では、出力は次のようになります。

default
move
default
move
default
move

GCC 4.4.0 (MinGW) (-O2 -std=c++0x) では、出力は次のようになります。

default
copy
copy
copy

何が起こっていて、どうすれば修正できますか? コピーは実際のクラスに対して高価であり、デフォルトの構築と移動は安価です。

4

5 に答える 5

18

どちらの実装 (Visual C++ 2010 と GCC 4.4.0) もエラーです。正しい出力は次のとおりです。

default
default
default

これは 23.3.5.1 [vector.cons]/4 で指定されています。

必須: T は DefaultConstructible でなければなりません。

実装では、A が MoveConstructible または CopyConstructible であると想定することはできません。

于 2011-02-01T17:18:03.003 に答える
6

問題は、使用している g++ のバージョンに C++0x に完全に準拠したライブラリがないことです。特に、C++03 では、std::vector のサイズ コンストラクターには次のシグネチャがあります。

// C++ 03
explicit vector(size_type n, const T& value = T(),
const Allocator& = Allocator());

その関数シグネチャと呼び出しにより、一時が作成され、定数参照によってバインドされ、そのコピーが要素ごとに作成されます。

一方、C++0x にはさまざまなコンストラクターがあります。

// C++0x
explicit vector(size_type n);
vector(size_type n, const T& value, const Allocator& = Allocator());

この場合、呼び出しは最初の署名と一致し、要素はデフォルトでコンテナー上に新しい配置で構築される必要があります (@Howard Hinnant が彼の回答で正しく指摘しているように、コンパイラーは移動コンストラクターをまったく呼び出すべきではありません)。

g++ のより新しいバージョンに更新された標準ライブラリがあるかどうかを試して確認するか、要素を手動で追加して問題を回避できます。

std::vector<A> v;
v.reserve( 3 );     // avoid multiple relocations
while (v.size() < 3 ) v.push_back( A() );
于 2011-02-01T17:18:34.090 に答える
1

次にこれを試してください:

std::vector<A> vec_a;
vec_a.reserve(3);
for (size_t i = 0; i < 3; ++i)
  vec_a.push_back(A());

あなたがやろうとしているのは、初期化プロセスで、構築してからコピー/コピー/コピーするのではなく、各値に構築+移動を使用するように強制することです。これらは単に異なる哲学です。ライブラリの作成者は、特定のタイプに対してどれが最適かを知ることはできませんでした。

于 2011-02-01T17:13:49.560 に答える
1

デフォルトで構築されたオブジェクトを「this」オブジェクトにコピーするときに、ctor アルゴリズムをコピーする特別な (安価な) ケースを追加できます。これは単なる回避策ですが、動作は十分に奇妙です。両方のコンパイラ (ライブラリ) がスタック上に一時オブジェクトを作成し、gcc がこの一時オブジェクトをターゲットに 3 回コピーします。msvc は一時オブジェクトを 3 回 (!) (スタック上にも) 再作成し、3 回 n 回ターゲットに移動します。オブジェクトを直接作成しない理由がわかりません。

于 2011-02-01T18:15:00.490 に答える
0

3 つのバリアントはすべて C++0x ドラフトに違反していないと思います。以下が必要です: 1. n 個の値で初期化された要素を持つベクトルを構築します 2. T は DefaultConstructible でなければなりません 3. n で線形です

デフォルト + コピー、デフォルト + 移動はデフォルトと同等であるため、3 つのバリアントはすべて 1 を満たします。 3 つのバリアントはすべて 3 を満たします。 3 つのバリアントはすべて 2 を満たします。Moveable 型には特定のアルゴリズムを使用できます。STL では、さまざまな機能を持つ型に対してさまざまなバージョンのアルゴリズムを使用するのが一般的です。

于 2011-02-08T15:52:58.057 に答える