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の場合、オブジェクトはlist-initialized(8.5.4)です。
候補を検討するときfunction
、コンパイラーは初期化子リスト(まだ型がありません-これは単なる文法構造です!)を引数として、そしてastd::vector<std::string>
をパラメーターとして見ますfunction
。変換のコストとは何か、そしてオーバーロードのコンテキストでこれらを変換できるかどうかを理解するために、13.3.3.1/5
13.3.3.1.5/1
:
引数が初期化子リスト(8.5.4)の場合、それは式ではなく、パラメーター型への変換には特別な規則が適用されます。
13.3.3.1.5/3
:
それ以外の場合、パラメーターが非集約クラスXであり、13.3.1.7によるオーバーロード解決が、引数初期化子リストからタイプXのオブジェクトの初期化を実行するために、Xの単一の最良のコンストラクターを選択する場合、暗黙の変換シーケンスはユーザーです。定義された変換シーケンス。13.3.3.1に記載されている場合を除き、初期化子リスト要素をコンストラクターパラメーター型に変換するには、ユーザー定義の変換が許可されます。
非集約クラスX
は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.3.1/4
、これは、複数のユーザー定義の変換を禁止する責任がある段落です。リストの初期化のみを見ていきます。
ただし、初期化子リストを単一の引数として渡す場合、または初期化子リストに要素が1つしかない場合は、[...] 13.3.1.7の候補であるユーザー定義の変換関数[(またはコンストラクター)]の引数を検討する場合また、あるクラスXへの変換または(おそらくcv修飾された)Xへの参照は、Xのコンストラクターの最初のパラメーターとして考慮されます。または[...]、標準の変換シーケンスと省略形の変換シーケンスのみが許可されます。
これは重要な制限であることに注意してください。これがなかった場合、上記はコピーコンストラクターを使用して同様に適切な変換シーケンスを確立でき、初期化はあいまいになります。(そのルールの「AまたはBとC」の潜在的な混乱に注意してください:「(AまたはB)とC」と言うことを意味します-したがって、パラメーターがタイプX
)。
13.3.1.7
この変換を行うために使用できるコンストラクターを収集するために委任されています。8.5
この段落に、私たちを委任した一般的な側面からアプローチしてみましょう8.5.4
。
8.5.4/1
:
リストの初期化は、直接初期化またはコピー初期化のコンテキストで発生する可能性があります。直接初期化コンテキストでのリスト初期化は直接リスト初期化と呼ばれ、コピー初期化コンテキストでのリスト初期化はコピーリスト初期化と呼ばれます。
8.5.4/2
:
コンストラクターは、その最初のパラメーターがタイプであるか、あるタイプEに対してcv修飾されている可能性がある参照であり、他のパラメーターがないか、他のすべてのパラメーターにデフォルトの引数(8.3.6)がある場合、初期化子リストコンストラクターです。std::initializer_list<E>
std::initializer_list<E>
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に変換するために必要な最悪の変換です。この変換はユーザーである可能性があります-初期化子リストコンストラクターの呼び出しのコンテキストでも定義された変換。
これで、初期化子リストが正常に変換され、変換シーケンスはユーザー定義の変換(fromchar const[N]
からstd::string
)になります。これがどのように行われるかについては、8.5.4
もう一度詳しく説明します。
それ以外の場合、Tがの特殊化でstd::initializer_list<E>
ある場合、initializer_listオブジェクトは以下に説明するように構築され、同じタイプのクラス(8.5)からのオブジェクトの初期化のルールに従ってオブジェクトを初期化するために使用されます。(...)
8.5.4/4
この最後のステップがどのように行われるかをご覧ください:)