13.3.3.1p4 の当初の意図は、12.3p4 で次の要件を適用する方法を説明することです。
4 - 多くても 1 つのユーザー定義の変換 (コンストラクターまたは変換関数) が暗黙的に単一の値に適用されます。
欠陥 84の前は、 13.3.3.1p4 はほとんど純粋に有益でした:
4 - ユーザー定義の変換による初期化のコンテキスト (つまり、ユーザー定義の変換関数の引数を考慮する場合。13.3.1.4 [over.match.copy]、13.3.1.5 [over.match.conv] を参照) )、標準の変換シーケンスと省略記号の変換シーケンスのみが許可されます。
これは、13.3.1.4 パラグラフ 1 の箇条書き 2 および 13.3.1.5p1b1 が候補関数を type を生成するクラスの関数に制限しているためS
です。別のユーザー定義の変換シーケンスが挿入されるようにします。(13.3.1.4p1b1 は別の問題です。以下を参照してください)。T
S
T
欠陥 84は、クラスのコピー初期化の 2 番目のステップで、コンストラクターの 1 つのパラメーターに許容される変換シーケンスを制限することによって (つまり、2 つの変換関数と変換コンストラクターを介してauto_ptr
) 抜け穴を修復しました(ここでは、取得のコンストラクターの使用を禁止します)。から引数を変換する変換関数):auto_ptr<Derived> -> auto_ptr<Base> -> auto_ptr_ref<Base> -> auto_ptr<Base>
auto_ptr<Base>
auto_ptr_ref<Base>
auto_ptr<Base>
4 - ただし、13.3.1.3 [over.match.ctor] で候補となるユーザー定義の変換関数の引数を考慮する場合、クラスのコピー初期化の 2 番目のステップで一時的なコピーのために呼び出される場合、またはすべての場合において、13.3.1.4 [over.match.copy]、13.3.1.5 [over.match.conv]、または 13.3.1.6 [over.match.ref] により、標準の変換シーケンスと省略記号変換シーケンスのみが許可されます。
n2672は次に追加します。
[...] by 13.3.1.7 [over.match.list] 初期化子リストを単一の引数として渡す場合、または初期化子リストに要素が 1 つだけあり、クラス X への変換または (おそらく cv 修飾された) への参照がある場合X は、X のコンストラクターの最初のパラメーターと見なされます [...]
13.3.1.3 および 13.3.1.7 で候補となる唯一の変換はコンストラクターであり、変換関数ではないため、これは明らかに混乱しています。 欠陥 978はこれを修正します。
4 - ただし、コンストラクターまたはユーザー定義の変換関数の引数を考慮すると [...]
これにより、13.3.1.4p1b1 は 12.3p4 と一貫性が保たれます。そうしないと、コピー初期化でコンストラクターの変換を無制限に適用できるからです。
struct S { S(int); };
struct T { T(S); };
void f(T);
f(0); // copy-construct T by (convert int to S); error by 12.3p4
問題は、13.3.1.7 を参照する言語が何を意味するかです。 X
コピーまたはムーブが構築されているため、言語はユーザー定義の変換を適用してそのX
引数に到達することを除外しています。 std::initializer_list
変換機能がないため、言語は他の何かに適用することを意図している必要があります。変換関数を除外する意図がない場合は、変換コンストラクターを除外する必要があります。
struct R {};
struct S { S(R); };
struct T { T(const T &); T(S); };
void f(T);
void g(R r) {
f({r});
}
リストの初期化に使用できるコンストラクターは 2 つあります。T::T(const T &)
とT::T(S)
。コピー コンストラクターを考慮から除外することにより (その引数はユーザー定義の変換シーケンスを介して変換する必要があるため)、正しいT::T(S)
コンストラクターのみが考慮されるようにします。この言語がないと、リストの初期化があいまいになります。初期化子リストを単一の引数として渡すと、同様に機能します。
struct U { U(std::initializer_list<int>); };
struct V { V(const V &); V(U); };
void h(V);
h({{1, 2, 3}});
編集:そして、すべてを経て、この分析を確認するJohannes Schaubによる議論を見つけました:
これは、ネストされたユーザー定義の変換を使用できるため、最初にコピー コンストラクターを呼び出してから、もう一方に対して行ったのと同じことを行うことで、あいまいな 2 番目の変換パスを常に生成できるため、リストの初期化のためにコピー コンストラクターを除外することを目的としています。変換。
では、不具合報告を送信します。13.3.3.1p4 の分割を提案します。
4 - ただし、候補であるコンストラクターまたはユーザー定義の変換関数の引数を考慮する場合:
- 13.3.1.3 [over.match.ctor] により、クラスのコピー初期化の 2 番目のステップで一時的なコピーのために呼び出された場合、または
- すべての場合において、13.3.1.4 [over.match.copy]、13.3.1.5 [over.match.conv]、または 13.3.1.6 [over.match.ref] によって、
標準の変換シーケンスと省略記号の変換シーケンスのみが考慮されます。13.3.1.7 [over.match.list] で候補となるクラスのコンストラクターの最初の引数を考慮するX
場合 イニシャライザー リストを単一の引数として渡す場合、またはイニシャライザー リストに要素が 1 つだけある場合、ユーザー定義の変換toX
または reference to (おそらくcv修飾)X
は、そのユーザー定義の変換が変換関数によって指定されている場合にのみ考慮されます。[注:リスト初期化のコンテキストでは、暗黙的な変換シーケンスで複数のユーザー定義の変換が許可されるため、この制限は、 の変換コンストラクターがX
単一の引数で呼び出されることを保証するために必要です。a
型X
または から派生した型ではない は、 から構築された一時オブジェクト自体X
で呼び出された のコンストラクターに対してあいまいではありません。-- 巻末注記】X
X
a