2011-02-01 9 views
22

私はlinux &ウィンドウ用のアプリケーションを作成していますが、GCCビルドがコピーコンストラクタに大量の無駄な呼び出しを生成していることに気付きました。この試験は、わずか3つの要素のベクトルを作成標準ライブラリコンテナは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); 
} 

ここでこの動作を生成するためのコード例です。私はAの値がないので、3つのデフォルトのコンストラクタ呼び出しと0のコピーを期待しています。ビジュアルC++ 2010

、出力される。

GCC 4.4.0(MinGWの)において
default 
move 
default 
move 
default 
move 

、(-O2 -std = C++ 0X)、出力は次のとおり

default 
copy 
copy 
copy 

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

+1

」ヘッダーを見ましたか?コンストラクタは何をしていますか? GCCの動作は、オブジェクトがデフォルトで構築されてからN回コピーされるC++ 03仕様と一貫しています。あなたのバージョンの標準ライブラリはC++ 0xに追加された新しいコンストラクタをサポートしていない可能性があります。デフォルトではN要素が構築されています。 –

+1

この例ではC++ 0x言語機能が使用されているため、これはC++ 0x仕様ではなく、C++ 03仕様に関する質問であると仮定しています。 –

+1

GCC 4.5と同じ出力 – tstenner

答えて

18

いずれの実装(Visual C++ 2010およびGCC 4.4.0)でもエラーが発生しています。正しい出力は次のとおり

default 
default 
default 

これは23.3.5.1 [vector.cons]/4で指定される:

要件:TはDefaultConstructibleなければなりません。

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

+5

+1、それのVS側で良いキャッチ。 –

+0

あなたは正しいです、私の次の質問は、VCがまったく構築しない理由です。何かできることはありますか? stlportまたはeaの実装がより適合しているかどうか – Inverse

+0

このstd :: lib:http://libcxx.llvm.org/は正解ですが、Windowsに移植されていません。 Windowsへの移植に関心のある当事者や、疑いなくヘルプを利用できる人がいます。 –

1

これを試してみてください:

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

あなたが何をしようとしては+構文を使用する代わりに、構造体の各値のために移動してから/コピー/コピーをコピーするために初期化プロセスを強制です。これらは単に異なる哲学です。図書館の作者は、どの型のものが最良であるかを知ることはできませんでした。

+4

これは異なる哲学以上のものです。 C++ 0xのドラフトで正しい動作が指定されています。 –

+1

+1、提案されたコードは実際には回避策です。 –

6

あなたが持っているg ++のバージョンには、C++ 0x完全準拠のライブラリがないという問題があります。具体的には、C++ 03、STDの大きコンストラクタで::ベクトルは次のシグネチャがあります。その関数のシグネチャとお電話で

// 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氏は、彼のanswerで、移動コンストラクタをまったく呼び出さないことを正しく指摘しています。あなたがしようとgのより新しいバージョンが更新され、標準ライブラリを持っ++、または手動で要素を追加することで問題を回避できるかどうかを確認することができます

std::vector<A> v; 
v.reserve(3);  // avoid multiple relocations 
while (v.size() < 3) v.push_back(A()); 
1

あなたはに特別な(安価な)ケースを追加することができますデフォルト構成オブジェクトを "this"オブジェクトにコピーするときにctorアルゴリズムをコピーします。これは単なる回避策ですが、その動作は不思議です。 両方のコンパイラ(ライブラリ)がスタック上に一時オブジェクトを作成した後、gccはこの一時的なものをターゲットに3回コピーします。 msvcは一時オブジェクトを3回(!)スタック上に再作成し、ターゲットに3回移動します。なぜ彼らはオブジェクトを直接作成しないのか分かりません。

0

私は、3つすべての亜種がC++ 0xドラフトに違反していないと思います。 1.構築物をベクトルn値初期化要素と 2. Tデフォルトの+コピー、デフォルト+ムーブと等価であるように、すべての3つの変異体は、1を満たすN

に 3リニアDefaultConstructibleなければならない:これは、以下の必要default 3つのバリエーションすべてが3を満たします 3つのバリエーションはすべて2を満たします:DefaultConstructibleタイプで動作します。 Moveable型には特定のアルゴリズムを使用できます。 STLでは、機能の異なるタイプに対して異なるバージョンのアルゴリズムを使用するのが一般的な方法です。

関連する問題