5

次のコードはで正常にコンパイルされg++ (GCC) 4.7.1 20120721ますが、最近のビルドでは失敗しますclang version 3.2 (trunk)

struct Y {};

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

void f(Y&& y) {}

int main()
{
  f(X());
  return 0;
}

変換演算子をに変更するoperator Y() constだけで、両方のコンパイラでコードをコンパイルできます。

この場合、実際に標準に準拠しているコンパイラはどれですか?規格はこれについて実際に何と言っていますか?

要求された逐語的なエラー:

bla.cpp:14:5: error: no viable conversion from 'X' to 'Y'
  f(X());
    ^~~
bla.cpp:1:8: note: candidate constructor (the implicit copy constructor) not viable: no known conversion from 'X' to
      'const Y &' for 1st argument
struct Y {
       ^
bla.cpp:1:8: note: candidate constructor (the implicit move constructor) not viable: no known conversion from 'X' to
      'Y &&' for 1st argument
struct Y {
       ^
bla.cpp:6:3: note: candidate function
  operator const Y() const { return Y(); }
  ^
bla.cpp:10:12: note: passing argument to parameter 'y' here
void f(Y&& y) {}
       ^

編集:残念ながら、オーバーロードを追加することさえ

void f(const Y&) {}

それでもclangに右辺値参照のオーバーロードを選択させるので、これは、たとえば標準のコンテナーで正常にコンパイルするために使用された既存のコードを壊します。

4

2 に答える 2

6

これを拒否するのはclangが正しいと思います。引数を に渡すには、f(Y&&)2 つの変換手順が必要です。最初の変換手順はあなたのものoperator const Y()で、2 番目の変換手順はYのコピー コンストラクタです。どちらもユーザー定義の変換としてカウントされ、両方とも暗黙的であると思います。これは、暗黙的な変換シーケンスにはユーザー定義の変換が 1 つしか含まれないという原則に違反しています。

const値で返すこの目的は?を返すセマンティクスに関するいくつかの興味深い洞察が含まれていconst Tます。

うーん、編集された質問のようにオーバーロードを追加しようとするとvoid f(const Y&y)、clang はかなり奇妙に動作します。に変換できないことについてまだ不平を言っており、診断に過負荷がリストされていませんX。しかし、オーバーロードを値で受け取るように変更すると、つまり write 、あいまいな呼び出しについて不平を言います。Yf(const Y& y)Yvoid f(const Y y)f

これは XCode 4.5 の clang で報告されますApple clang version 4.1 (tags/Apple/clang-421.11.66) (based on LLVM 3.1svn)。バニラのclangでこれを再現できる場合は、おそらくclangメーリングリストでこれを報告する必要があります-確かにどこかにバグが潜んでいるようです...

于 2012-10-08T11:16:43.263 に答える
2

この例は形式が正しくありません。

N3242の引用。

8.5.3 パラグラフ 4:

型「<em>cv1 T1」および「<em >cv2」が与えられた場合T2、「<em>cv1 」は「<em>cv2 」と同じ型であるか、またはの基底クラスである場合T1に参照関連です。「<em>cv1 」は、「<em>cv2 」と参照互換であり、 cv1 がcv2と同じ cv-qualification であるか、 cv2より大きい cv-qualification である場合に参照互換性があります。T2T1T2T1T2T1T2T1T2

パラグラフ5(箇条書きは私のものです):

型 " cv1 "への参照は、次のように型 " cv2T1 "の式によって初期化されます。 T2

  1. 参照が左辺値参照であり、...
  2. それ以外の場合、参照は非揮発性 const 型への左辺値参照 (つまり、 cv1const) であるか、参照は右辺値参照でなければなりません。

    a. 初期化式の場合

    • 私。が xvalue、クラス prvalue、配列 prvalue または関数左辺値であり、" cv1 " が " cv2 " T1と参照互換性がある、または T2
    • ii. はクラス型 (つまりT2、 はクラス型) を持ち、T1は に参照関連ではなく、型 " cv3T2 "の xvalue、クラス prvalue、または関数左辺値に暗黙的に変換できます。ここで、" cv1 " は と参照互換性があります。 " cv3 ", T3 T1 T3
    • 次に、参照は .... にバインドされます。

    b. それ以外の場合は、型 " cv1 T1 " のテンポラリが作成され、非参照コピー初期化 (8.5) の規則を使用して初期化子式から初期化されます。その後、参照は一時にバインドされます。T1が に関連する参照である場合T2cv1は cv2と同じ cv-qualification またはそれ以上の cv-qualification でなければなりませ。...

関数パラメーターの初期化では、 T1is YT2is X、およびcv1cv2は両方とも空です。1 が出ています: 参照は右辺値参照であり、左辺値参照ではありません。2.ai が出ています:Xとの参照互換性がありませんY。2.b. Ytype の prvalue から をコピー初期化するにXは、変換関数と のコピー コンストラクタの 2 つのユーザー定義の変換が必要になるため、 はアウトですY。(正確な禁止事項は 13.3.3.1p4 にあります。)

ケース 2.a.ii. の場合、" cv3 T3 " の当然の選択は ですが、 は との参照互換性がないconst Yため、これは適切ではありません。isとcv3 is emptyを試すことを主張するかもしれませんが、2 番目の暗黙的なユーザー定義の変換としてのコピー コンストラクターが必要になることに戻ります。Yconst YT3YY

于 2012-10-08T13:02:07.620 に答える