web-dev-qa-db-ja.com

関数引数としてのstd :: initializer_list

なぜかC++ 0xはstd::initializer_listから構築できる型を期待する関数の関数引数として。たとえば、std::vector。しかし、どうやら、それは動作しません。これは私のコンパイラだけですか、それとも動作しませんか?潜在的な過負荷解決の問題が原因ですか?

#include <string>
#include <vector>

void function(std::vector<std::string> vec)
{
}

int main()
{
    // ok
    std::vector<std::string> vec {"hello", "world", "test"};

    // error: could not convert '{"hello", "world", "test"}' to 'std::vector...'
    function( {"hello", "world", "test"} );
}
35
fredoverflow

GCCにはバグがあります。標準はこれを有効にします。見る:

これには2つの側面があることに注意してください。

  • 一般に、どのように、どのような初期化が行われますか?
  • 過負荷の解決時に初期化はどのように使用されますか?

最初の質問は、セクション8.5で回答されています。 2番目の質問は、セクション13.3で回答されています。たとえば、参照バインディングは8.5.3および13.3.3.1.4で処理され、リストの初期化は8.5.4および13.3.3.1.5で処理されます。

8.5/14,16

フォームで発生する初期化

T x = a;

および引数の引き渡しの場合、関数の戻り、例外のスロー(15.1)、例外の処理(15.3)、および集約メンバーの初期化(8.5.1)は、コピー初期化と呼ばれます。


イニシャライザのセマンティクスは次のとおりです[...]:イニシャライザがbraced-init-listの場合、オブジェクトはリストで初期化されます(8.5.4)。

候補functionを検討する場合、コンパイラーは引数として初期化リスト(タイプはまだありません-単なる文法構造です!)を引数として、functionを表示します。 var] _。変換のコストと、オーバーロードのコンテキストで変換できるかかどうかを把握するには、std::vector<std::string>

13.3.3.1/5

引数が初期化リスト(8.5.4)の場合、それは式ではなく、パラメーター型に変換するための特別な規則が適用されます。

13.3.3.1.5/1

それ以外の場合、パラメーターが非集約クラスXであり、13.3.1.7に基づくオーバーロード解決がXの単一の最良のコンストラクターを選択して、引数初期化子リストからX型のオブジェクトの初期化を実行する場合、暗黙の変換シーケンスはユーザー定義された変換シーケンス。ユーザー定義の変換は、13.3.3.1に記載されている場合を除き、初期化子リスト要素をコンストラクターパラメーター型に変換できます。

非集合クラスX13.3.3.1.5/3であり、以下の単一の最良のコンストラクターを見つけます。最後のルールは、次のような場合にユーザー定義の変換を使用することを許可します。

std::vector<std::string>

ユーザー定義の変換が必要な場合でも、文字列リテラルをstruct A { A(std::string); A(A const&); }; void f(A); int main() { f({"hello"}); } に変換できます。しかし、それは別の段落の制限を指摘しています。 std::stringは何と言いますか?

13.3.3.1:複数のユーザー定義の変換を禁止する段落です。リストの初期化のみを見ていきます。

ただし、[...]による候補であるユーザー定義の変換関数[(またはコンストラクタ)]の引数を検討する場合13.3.1.7初期化リストを単一の引数として渡す場合、または初期化リストに要素が1つしかない場合あるクラスXへの変換または(おそらくcv修飾された)Xへの参照は、Xのコンストラクターの最初のパラメーター、または[...]と見なされます。

これが重要な制限であることに注意してください。そうでない場合、上記はコピーコンストラクターを使用して同等に適切な変換シーケンスを確立でき、初期化があいまいになります。 (そのルールでの「AまたはBおよびC」の混乱の可能性に注意してください:「(AまたはB)およびC」と言うことを意味しているため、のみX型のパラメーターを持つXのコンストラクターによって変換しようとしています。

この変換に使用できるコンストラクターを収集するために、13.3.3.1/4に委任されます。 13.3.1.7から8.5に委任された8.5.4から始まる一般的な側面からこの段落に取り組みましょう。

8.5.4/1

リストの初期化は、直接初期化またはコピー初期化のコンテキストで発生します。直接初期化コンテキストでのリスト初期化は直接リスト初期化と呼ばれ、コピー初期化コンテキストでのリスト初期化はコピーリストと呼ばれます-initialization

8.5.4/2

最初のパラメーターがタイプstd::initializer_list<E>であるか、または何らかのタイプEのcv修飾されたstd::initializer_list<E>への参照である場合、コンストラクターはイニシャライザーリストコンストラクターであり、他のパラメーターがないか、他のすべてのパラメーターにデフォルトの引数があります(8.3.6)。

8.5.4/3

T型のオブジェクトまたは参照のリスト初期化は、次のように定義されます。[...]それ以外の場合、Tがクラス型の場合、コンストラクターが考慮されます。 Tに初期化子リストコンストラクタがある場合、引数リストは初期化子リストを単一の引数として構成します。それ以外の場合、引数リストは初期化リストの要素で構成されます。該当するコンストラクターが列挙され(13.3.1.7)、最適なコンストラクターがオーバーロード解決(13.3)によって選択されます。

現時点では、Tはクラスタイプstd::vector<std::string>です。引数が1つあります(まだ型がありません!文法的な初期化子リストがあるという状況にあります)。コンストラクタは13.3.1.7以降で列挙されます。

[...] Tに初期化リストコンストラクタ(8.5.4)がある場合、引数リストは単一の引数としての初期化リストで構成されます。それ以外の場合、引数リストは初期化リストの要素で構成されます。 copy-list-initializationの場合、候補関数はすべてTのコンストラクターです。ただし、明示的なコンストラクターを選択すると、初期化の形式が正しくありません。

std::vectorのイニシャライザリストを唯一の候補と見なします。これは、他の人がそれに勝つかないか、引数に適合しないことをすでに知っているためです。次の署名があります。

vector(initializer_list<std::string>, const Allocator& = Allocator());

現在、イニシャライザリストをstd::initializer_list<T>に変換するルール(引数/パラメータ変換のコストを分類するため)は、13.3.3.1.5に列挙されています。

引数が初期化リスト(8.5.4)の場合、それは式ではなく、パラメーター型に変換するための特別な規則が適用されます。 [...]パラメータタイプがstd::initializer_list<X>であり、初期化リストのすべての要素を暗黙的にXに変換できる場合、暗黙の変換シーケンスは、リストの要素をXに変換するために必要な最悪の変換です。 この変換は、ユーザー定義の変換にすることができます初期化リストコンストラクターの呼び出しのコンテキストでも。

これで、初期化リストが正常に変換され、変換シーケンスはユーザー定義の変換(char const[N]からstd::stringへ)になります。これがどのように行われるかについては、再度8.5.4で詳しく説明します。

それ以外の場合、Tがstd::initializer_list<E>の特殊化である場合、initializer_listオブジェクトは以下に説明するように構築され、同じタイプのクラスからのオブジェクトの初期化のルールに従ってオブジェクトを初期化するために使用されます(8.5)。 (...)

この最後のステップがどのように行われるかについては8.5.4/4をご覧ください:)

それはこのように機能するようです:

function( {std::string("hello"), std::string("world"), std::string("test")} );

多分それはコンパイラのバグですが、おそらくあなたはあまりにも多くの暗黙の変換を求めています。

3
UncleBens

奇妙なことですが、ここでは何が起こっているのかと思います。initializer_listへの変換は1つの変換であり、vectorへの変換は別の変換です。その場合は、暗黙的な変換が1つだけという制限を超えています...

2
Jerry Coffin

これはコンパイラのバグか、コンパイラがstd :: initializer_listをサポートしていないかのいずれかです。 GCC 4.5.1でテストされ、正常にコンパイルされます。

1
Ricky65

初期化子リストのタイプを指定する必要があります

function(std::initializer_list<std::string>{"hello", "world", "test"} );

幸運を

0
fnc12