2012-01-09 6 views
45

次のコード:GCCでコンパイルしたときemplace_back()が均一な初期化を使用しないのはなぜですか?

#include <vector> 

struct S 
{ 
    int x, y; 
}; 

int main() 
{ 
    std::vector<S> v; 
    v.emplace_back(0, 0); 
} 

は、次のエラーを与える:

vectoremplace_back()に引数から要素を構築するために、定期的 ()コンストラクタ構文を使用していることを示唆している
In file included from c++/4.7.0/i686-pc-linux-gnu/bits/c++allocator.h:34:0, 
       from c++/4.7.0/bits/allocator.h:48, 
       from c++/4.7.0/vector:62, 
       from test.cpp:1: 
c++/4.7.0/ext/new_allocator.h: In instantiation of 'void __gnu_cxx::new_allocator<_Tp>::construct(_Up*, _Args&& ...) [with _Up = S; _Args = {int, int}; _Tp = S]': 
c++/4.7.0/bits/alloc_traits.h:265:4: required from 'static typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type std::allocator_traits<_Alloc>::_S_construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = S; _Args = {int, int}; _Alloc = std::allocator<S>; typename std::enable_if<std::allocator_traits<_Alloc>::__construct_helper<_Tp, _Args>::value, void>::type = void]' 
c++/4.7.0/bits/alloc_traits.h:402:4: required from 'static void std::allocator_traits<_Alloc>::construct(_Alloc&, _Tp*, _Args&& ...) [with _Tp = S; _Args = {int, int}; _Alloc = std::allocator<S>]' 
c++/4.7.0/bits/vector.tcc:97:6: required from 'void std::vector<_Tp, _Alloc>::emplace_back(_Args&& ...) [with _Args = {int, int}; _Tp = S; _Alloc = std::allocator<S>]' 
test.cpp:11:24: required from here 
c++/4.7.0/ext/new_allocator.h:110:4: error: new initializer expression list treated as compound expression [-fpermissive] 
c++/4.7.0/ext/new_allocator.h:110:4: error: no matching function for call to 'S::S(int)' 
c++/4.7.0/ext/new_allocator.h:110:4: note: candidates are: 
test.cpp:3:8: note: S::S() 
test.cpp:3:8: note: candidate expects 0 arguments, 1 provided 
test.cpp:3:8: note: constexpr S::S(const S&) 
test.cpp:3:8: note: no known conversion for argument 1 from 'int' to 'const S&' 
test.cpp:3:8: note: constexpr S::S(S&&) 
test.cpp:3:8: note: no known conversion for argument 1 from 'int' to 'S&&' 

。なぜ、vectorは、上記のような例を作るために、{}均一初期化構文を代わりに使用しないのですか?

{}(1つではコンストラクタが呼び出されますが、1つではない場合でも動作します)を使用すると失うものはないようですが、C++の精神{}を使用する11 - つまり、どこにでも - - オブジェクトを初期化するために、すべての後、均一初期の全体のポイントは、均一に使用されることです。

答えて

42

偉大な心は似ていると思います。私は欠陥レポートを提出し、この非常に重要なトピックについて標準への変更を提案しました。 Direct vs uniform initialization in std::allocator

http://cplusplus.github.com/LWG/lwg-active.html#2089

また、リュックダントンは私が難しさを理解して助けました。

EmplaceConstructible(23.2.1 [container.requirements.general]/13)要件を オブジェクトを初期化するために使用される場合、直接初期化が起こります。集合体または をemplaceでstd :: initializer_listコンストラクタを使用して初期化するには、初期化された型を と命名し、一時的に移動する必要があります。これは の結果です。 リスト初期化(「均一初期化」とも呼ばれます) 構文ではなく、直接初期化を使用したstd :: allocator :: construct。 STDを変更

::アロケータは:: は、とりわけ、はstdする優先順位を与えるだろうリストの初期化を使用するように構築:: initializer_list コンストラクタのオーバーロードを、直感的かつ 修理不可能な方法で有効なコードを壊す - なしが存在することになりますemplace_backが コンストラクタにアクセスして、本質的に がpush_backを再実装せずにstd :: initializer_listによってプリエンプトされました。

std::vector<std::vector<int>> v; 
v.emplace_back(3, 4); // v[0] == {4, 4, 4}, not {3, 4} as in list-initialization 

提案妥協がSTD :: is_constructible、直接初期化が十分に形成されているかどうかをテスト とSFINAEを使用することです。 is_constructibleがfalseの場合、代替 のstd ::アロケータは::過負荷が リスト初期化を使用していますが選択される構築します。リスト初期化は常に ダイレクト初期化になりますので、直接初期化のオーバーロードが失敗しないため、 リスト初期化(uniform-initialization)が常に使用されているかのような診断メッセージが表示されます( )。

私は、このスキームのギャップを公開する2つの角例を見ることができます。 1つは、 のstplace :: initializer_listの引数が のコンストラクタを満たしている場合(例えば、 の値を{3、4}にemplace-insertしようとした場合)です。回避策は、 v.emplace_back(std :: initializer_list(3,4))のように、 std :: initializer_listタイプを明示的に指定することです。これは、 のように、std :: initializer_listのようにセマンティクスが導かれたため、 のように見えます。

他のケースは、集約初期化のための引数がコンストラクタを満たしている場合です。集計にはユーザ定義の コンストラクタを使用できないため、 の最初の非静的データメンバーは集約タイプから暗黙的に変換可能であり、 は初期化リストに1つの要素があることが必要です。回避策は、 に2番目のメンバのイニシャライザを指定することです。 は、 によって1つの非静的データメンバーのみを持つ集約を組み込みの型から集約の型に変換することは不可能です。この は容認できるほど小さな穴のようです。

+2

構文補完のinitリストを完全に転送する言語補完型を発明できました。それでは、emplace_back({a、b、...})と言うことができます –

+4

@ JohannesSchaub-litb:IMHO、これは 'std :: initializer_list'と同じです。しかし、 'std :: allocator'を単純に変更するだけで問題は解決します。 – Potatoswatter

+0

vvuserがカスタムallocでベクトルを置き換えたい場合は、emplace_back(1,2、alloc)を書き込みます。それを({1,2}、alloc)として転送する方法をどのように知っていますか?アロケータのハックは、より多くの事柄を壊すことになります。適切な解決策を検討する必要があります。 –

関連する問題