6

このプログラムは合法ですか?

struct X { X(const X &); };
struct Y { operator X() const; };

int main() {
  X{Y{}};   // ?? error
}

n2672の後、および欠陥 978によって修正されたように、13.3.3.1 [over.best.ics]には次のものがあります。

4 - ただし、候補であるコンストラクターまたはユーザー定義の変換関数の引数を考慮する場合 [...] 13.3.1.7 [...] 初期化子リストに要素が 1 つだけあり、クラス X への変換がある場合または (cv 修飾されている可能性がある) X への参照は、X のコンストラクターの最初のパラメーターとして考慮されます [...]。標準の変換シーケンスと省略記号変換シーケンスのみが考慮されます。

これはかなりひねくれているようです。リスト初期化キャストを使用して変換を指定することは違法であるという結果になります。

void f(X);
f(Y{});     // OK
f(X{Y{}});  // ?? error

n2640を理解しているように、リスト初期化は直接初期化とコピー初期化のすべての使用を置き換えることができるはずですが、リスト初期化のみを使用してXタイプのオブジェクトからタイプのオブジェクトを構築する方法はないようですY:

X x1(Y{});  // OK
X x2 = Y{}; // OK
X x3{Y{}};  // ?? error

これが標準の実際の意図ですか。そうでない場合、どのように読むか、または読むべきですか?

4

2 に答える 2

6

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 は別の問題です。以下を参照してください)。TST

欠陥 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単一の引数で呼び出されることを保証するために必要です。aXまたは から派生した型ではない は、 から構築された一時オブジェクト自体Xで呼び出された のコンストラクターに対してあいまいではありません。-- 巻末注記XXa

于 2012-10-04T17:40:37.733 に答える
3

XCode 4.4 に同梱されている clang 3.1 のバージョンは、あなたの解釈に同意し、拒否しX{Y{}};ます。私と同様に、標準の関連部分を数回読み直した後、FWIW.

Xのコンストラクターを変更して、両方ともタイプの 2 つの引数を取るようにするconst X&と、clang はステートメント を受け入れますY y; X{y,y}。(しようとするとクラッシュしX{Y{},Y{}}ます...)。これは、単一要素の場合にのみユーザー定義の変換をスキップすることを要求する 13.3.3.1p4 と一致しているようです。

標準および省略記号変換シーケンスへの制限は、別のユーザー定義の変換が既に行われている場合にのみ最初に追加されたようです。または、少なくともhttp://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#84を読む方法です。

興味深いのは、標準がコピーの初期化の 2 番目のステップにのみ制限を適用するように注意を払っていることです。これは、既に正しい型を持っている (ユーザー定義の変換によって取得された可能性がある!) 一時的なものからコピーします。まだリストの初期化の場合、同様のメカニズムは存在しないようです...

于 2012-10-01T19:08:19.893 に答える