3

標準化後のドラフト n3376 には、ユーザー定義型への明示的な変換関数の使用例 (12.3.2:2) があります。

class Y { };
struct Z {
  explicit operator Y() const;
};
void h(Z z) {
  Y y1(z); // OK: direct-initialization
}

12.3.2:2 によると、明示的な変換関数は「直接初期化のためのユーザー定義の変換としてのみ考慮されます」。ただし、次のことを許可するように見えます。

struct Y { Y(int); };
struct Z {
  explicit operator int() const;
};
void h(Z z) {
  Y y1(z); // direct-initialization
}

これは標準の意図と矛盾しているように見え、実際には gcc-4.7.1 によって拒否されています。

source.cpp: In function 'void h(Z)':
source.cpp:4:9: error: no matching function for call to 'Y::Y(Z&)'
source.cpp:4:9: note: candidates are:
source.cpp:1:12: note: Y::Y(int)
source.cpp:1:12: note:   no known conversion for argument 1 from 'Z' to 'int'
source.cpp:1:8: note: constexpr Y::Y(const Y&)
source.cpp:1:8: note:   no known conversion for argument 1 from 'Z' to 'const Y&'
source.cpp:1:8: note: constexpr Y::Y(Y&&)
source.cpp:1:8: note:   no known conversion for argument 1 from 'Z' to 'Y&&'

ZgccからYviaへの変換を拒否するのは正しいですintか、それとも標準で実際にこの使用法が許可されていますか?

上記の直接初期化のコンテキストを検討しました。8.5:16 のクラス型への直接初期化の定義に従って、コンストラクターは初期化式を引数として呼び出されるため、暗黙的な変換シーケンス (13.3.3.1) によってパラメーター型に変換されます。暗黙的な変換シーケンスは暗黙的な変換 (4:3) であるため、直接初期化ではなくコピー初期化 (8.5:14) をモデル化するため、12.3.2:2 の言語は式全体を参照している必要があります。

これは 12.3:4 (複数のユーザー定義の変換) の違反ではないことにも注意してください。同じコンパイラは、削除された同じコードに満足していますexplicit(Clang と Comeau と同様):

struct Y { Y(int); };
struct Z { operator int(); };
void h(Z z) {
  Y y1(z); // direct-initialization
}

operator YJesse Good は 13.3.1.4:1 でとケースの違いを特定したと思いますがoperator int、私がまだ懸念している 3 番目のケースがあります。

struct X {};
struct Y { Y(const X &); };
struct Z {
  explicit operator X() const;
};
void h(Z z) {
  Y y1(z); // direct-initialization via class-type X
}

Xのコンストラクターの単一const X &パラメーターにバインドされる一時の初期化は、asおよびasYを使用して、13.3.1.4:1 に従って直接初期化コンテキストで処理されます。この条項は正しくないと思うので、次のように読むべきです。TXSZ

13.3.1.4 ユーザー定義変換によるクラスのコピー初期化 [over.match.copy]

1 - [...]オブジェクトの直接初期化のコンテキストで単一の引数で呼び出され、最初の引数としておそらくcv修飾された参照を受け取るコンストラクターの最初のパラメーターにバインドされる一時を初期化するときタイプ "cv2 "の場合、明示的な変換関数も考慮されます。[...]TT

混乱を避けるために、12.3.2:2 も修正する必要があると思います。

12.3.2 変換関数 [class.conv.fct]

2 - 変換関数は明示的 (7.1.2) である場合があり、その場合、特定のコンテキスト (13.3.1.4、13.3.1.5、13.3.1.6 ) での直接初期化 (8.5) のユーザー定義変換としてのみ考慮されます。 . [...]

上記について何かコメントはありますか?

4

3 に答える 3

4

8.5 および 13.3.1.3 によると、コンストラクターYが考慮され、オーバーロードの解決によって最適なコンストラクターが選択されます。この場合、関連するコンストラクターはY(int);、copy および move コンストラクターです。オーバーロード解決の過程で 13.3.2 実行可能な関数 [over.match.viable] はこれを指定します:

3 第二にF、実行可能な関数であるためには、引数ごとに、その引数を の対応するパラメーターに変換する暗黙の変換シーケンス(13.3.3.1) が存在する必要がありますF。[...]

これらすべてのコンストラクターについて、 からのフレーバーのZいずれかまたは 1 つへのそのような変換はありません。自分自身を納得させるために、13.3.3.1 暗黙の変換シーケンス [over.best.ics] で暗黙の変換シーケンスについて標準が何を述べているかを調べてみましょう。intY

1 暗黙的な変換シーケンスは、関数呼び出しの引数を、呼び出される関数の対応するパラメーターの型に変換するために使用される一連の変換です。一連の変換は、4 節で定義されている暗黙の変換です。つまり、単一の式によるオブジェクトまたは参照の初期化の規則によって管理されます (8.5、8.5.3)。

条項 4 を相互参照すると、暗黙的な変換がコピー初期化の観点から定義されていることがわかります (つまりT t=e;Tisinteis z)。

T t=e;(§4.3) 式 e は、何らかの発明された一時変数 t (8.5) に対して、宣言が整形式である場合にのみ、暗黙的に型 T に変換できます。[...]

したがって、12.3.2:2 をこの初期化に適用しないことにします。これは、直接初期化のより大きなコンテキストで発生します。そうしないと、この最後の段落と矛盾します。

于 2012-09-11T16:08:24.740 に答える
3

Luc Danton's answer に記載されているように、暗黙的な変換はコピーの初期化に関して定義されています。次に、13.3.1.4:1[ユーザー定義変換によるクラスのコピー初期化] を見ると、次のようになります。

初期化式の型がクラス型「cv S」 の場合、Sとその基底クラスの非明示的な変換関数が考慮されます。直接初期化のコンテキストで単一の引数を使用して呼び出される、最初の引数として cv 修飾された可能性のある T への参照を受け取るコンストラクターの最初のパラメーターにバインドされるように一時を初期化する場合、明示的な変換関数も考慮されます。S 内に隠されておらず、cv 修飾されていないバージョンがT と同じ型であるか、その派生クラスである型を生成する関数は候補関数です。. 「X への参照」を返す変換関数は、参照のタイプに応じて X 型の左辺値または x 値を返すため、候補関数を選択するこのプロセスで X を生成すると見なされます。

私がこれを正しく理解していれば、変換関数が aYを生成するため、最初のものは機能し、引用の2番目に強調された部分で示されているように候補関数ですが、2番目のケースでは、候補関数のセットは空です。最初の強調部分で示されているように、への変換Y関数も非明示的な変換関数もありません。

3番目のケースに関して:

欠陥レポート 1087を見つけた後、言及したように cv2 T のオブジェクトを直接初期化するときに、コンストラクターを許可、コピー、移動、およびテンプレート化することを意図していたことは明らかです。13.3.1.4 の最初の一節を読むと、 と書かれているので、あなたが言及してAssuming that “cv1 T” is the type of the object being initialized, with T a class typeいることを暗示していると思います。of an object of type "cv2 T"ただし、(読み返した後)、欠陥レポートによる変更により、文言が曖昧になり、提案された3番目のケースがカバーされていないようです。

于 2012-09-11T23:56:31.793 に答える
2

私は言語弁護士ではありませんが、標準の文言は、変換演算子を as としてマークするには、 object の初期化の一部としてexplicit変換タイプ (つまり ) を明示的に指定する必要があることを暗示しています。コード では、 variableに指定した型が であるため、暗黙的な変換に依存しているように見えます。 inty1Y y1(z)y1Y

したがって、この状況での明示的な変換演算子の正しい使用法は次のようになると思います。

Y y1( int(z) ); 

または、効果的にキャストを指定しているため、できれば

Y y1( static_cast<int> (z) ); 
于 2012-09-11T18:12:22.940 に答える