2016-04-05 11 views
6

私は次のように非常に単純なクラスを持っている:コピーコンストラクタがstd :: vectorのイニシャライザリストで呼び出されるのはなぜですか?

class Foo 
{ 
public: 
    Foo() {} 
    Foo(const Foo&) = delete; 
    Foo(Foo&&) {} 

    void operator=(const Foo&) = delete; 
    void operator=(Foo&&) {} 

    void dump() const {} 
}; 

クラスが構築可能と割り当て可能な移動ですが構築可能と割り当て可能なコピーではありません。

私はベクトルの初期化子リストを使ってFoo要素のベクトルを初期化したいと思います。

std::vector<Foo> vf = { Foo() }; 

コードでは、削除されたコピーコンストラクタを使用する必要があるため、コンパイラには不満があります。誰にも説明することができます、なぜこの構造では使用されていない、なぜオブジェクトのコピーが必要ですか?

次のようにもいずれかのコピーコンストラクタを必要と動作しません。一方

std::vector<Foo> vf = { std::move(Foo()) }; 

を、これは正常に動作(移動コンストラクタが呼ばれた)のための:

std::vector<Foo> vf; 
vf.push_back(Foo()); 

感謝説明... :)

更新:

提案はthis投稿は私の質問を説明します。

さらに者は、(一緒に上記class Fooと)、次のコードを考える:

class Bar { 
public: 
    Bar(std::initializer_list<Foo> _l) { 
     std::cout << "Bar::Bar()" << std::endl; 
     for (auto& e : _l) 
      e.dump(); 
    } 
}; 

int main() { 
    Bar b { Foo() }; 
    return 0; 
} 

これは、(C++ 11でコンパイルされた)次の出力を生成する:

Foo::Foo() 
Bar::Bar() 
Foo::dump() 
Foo::~Foo() 

それは見ることができ、イニシャライザーリストは実際には中括弧の間に記述された 'オブジェクトのコピー'で埋められていないことに注意してください。これはC++ 14では真実ではないかもしれません。

+2

これはあなたが必要とするすべての説明があります。http://stackoverflow.com/questions/8193102/initializer-list-and-move-semantics – bolov

+0

はい、それは説明しています。ありがとう。 – Peter

答えて

8

具体的には、コピーコンストラクタが使用されているinitializer listを使用しているためです。初期化子リストはオブジェクトのコピーで初期化され、ベクトルに渡されます。

リンクされたリファレンスを読めば、C++ 14からそれも明示的

を言う...各要素はコピー初期化です...元初期化子リスト

の対応する要素から

重点鉱山

+0

"初期化子リストはオブジェクトのコピーで初期化されています" - 実際はそうですか?ここでは、 "initializer_list return const T *の開始と終了"(http://stackoverflow.com/a/8193157/388661による)という問題はないので、イニシャライザリストから項目を削除することはできませんか? – davmac

+0

ええと、ちょうど簡単なテストをしました。単一のオブジェクトを含むイニシャライザリストを持つベクトルを初期化すると、実際にはコピーコンストラクタが_twice_と呼ばれるようになります。ここに言語デザインの問題があるように私には思われます! Upvoted。 – davmac

+0

@davmacまた、リファレンスから: "タイプ' std :: initializer_list 'のオブジェクトは、' const T'型のオブジェクトの**配列**へのアクセスを提供する軽量プロキシオブジェクトです。 "配列"使用されている要素はコピーする必要があり、移動を禁止する要素です。 –

0

std::initializer_listは移動のみのタイプのために動作しません。詳細については、this questionを参照してください。

幸い、死者簡単に修正はあなたのためにあります:

std::vector<foo> vf (1); 

これは1つのデフォルト-構築fooとベクトルを初期化します。

+0

さて、私のサンプルコードが単純すぎるかもしれません。このコードの目的は、純粋に 'vf'で単一要素を作成するのではなく、私の質問を示すことでした。 – Peter

0

一般的に、std::vectorの要素に課される要件は、要素の種類が完全な型であり、消去可能な要件を満たしていることです。したがって、あなたのオブジェクトには問題はありません。

しかし、など初期化リストで初期化:

std::vector<Foo> vf = { Foo() }; 

リスト内の一時オブジェクトFoo()ベクトルにコピーされることを要求します。したがって、すでにオブジェクトをコピーできないようにしているため、コンパイラは不平を言っています。

次のようにして目的を達成できます。

std::vector<Foo> vf(1); 
関連する問題