3

次のコード(GCC 4.3)を考えると、どちらの場合も参照への変換が呼び出されるのはなぜですか?

class A { };

class B {
public:
  operator A() {}
  operator A&() {}
};

int main() {
  B b;
  (A) b;
  (A&) b;
}

http://ideone.com/d6iF8

4

1 に答える 1

8

コードはあいまいであり、コンパイルしないでください(13.3.3:2に従って形式が正しくありません)。

左辺値から右辺値への変換はID変換と同じランクであるため、(13.3.3:1による)それらの間で選択する方法はありません。

Comeau C ++(おそらく最も標準に準拠したコンパイラ)は、次のエラーを出します。

"ComeauTest.c", line 11: error: more than one user-defined conversion from "B" to
          "A" applies:
            function "B::operator A()"
            function "B::operator A &()"
    (A) b;
        ^

標準からの関連テキストは次のとおりです。

13.3.3最高の実行可能な関数[over.match.best]

[...]これらの定義を考えると、実行可能な関数F1は、別の実行可能な関数F2よりも優れた関数であると定義されています[...]

2-他のすべての実行可能な関数よりも優れた関数である実行可能な関数が1つだけある場合、それは過負荷解決によって選択されたものです。そうしないと、呼び出しの形式が正しくありません。

定義自体は複雑ですが、ユーザー定義の変換で注意すべき点が2つあります。

S_a - U - S_b最初に、変換シーケンスとしてのユーザー定義変換の適用を指定して、標準変換シーケンス、ユーザー定義変換、別の標準変換シーケンスのシーケンスに分解します。これはすべての場合をカバーします。変換シーケンスに複数のユーザー定義の変換を含めることはできません。標準の変換シーケンスは「ID変換」、つまり変換は不要です。

次に、ユーザー定義の変換シーケンスを比較する場合、重要なのは2番目の標準変換シーケンスだけです。これは13.3.3にあります:

13.3.3最高の実行可能な関数[over.match.best]

[...]実行可能な関数F1は、[...]の場合、別の実行可能な関数F2よりも優れた関数であると定義されます。

  • コンテキストはユーザー定義の変換による初期化であり(8.5、13.3.1.5、および13.3.1.6を参照)、F1の戻りタイプから宛先タイプ(つまり、初期化されるエンティティのタイプ)への標準変換シーケンスは次のとおりです。 F2の戻りタイプから宛先タイプへの標準変換シーケンスよりも優れた変換シーケンス。

および13.3.3.2:

13.3.3.2暗黙の変換シーケンスのランク付け[over.ics.rank]

3-次の規則のいずれかが適用されない限り、同じ形式の2つの暗黙的な変換シーケンスは区別できない変換シーケンスです。[...]

  • ユーザー定義の変換シーケンスU1は、同じユーザー定義の変換関数またはコンストラクターまたは集約初期化を含み、U1の2番目の標準変換シーケンスが2番目の標準変換シーケンスよりも優れている場合、別のユーザー定義の変換シーケンスU2よりも優れた変換シーケンスです。 U2の。

したがって、変換シーケンスU1 = (S1_a - U'1 - S1_b)を比較する場合、重要なのはとU2 = (S2_a - U'2 - S2_b)の相対ランクだけです。ユーザー定義の変換のパラメーターに到達するために必要な標準の変換シーケンスは重要ではありません。S1_bS2_b

したがって、の可能な変換シーケンスは(A) b、を生成する変換シーケンスを必要とし、次のようになりB -> Aます。

U1: B -> B [identity], B::operator A() [user-defined], A -> A [identity]
U2: B -> B [identity], B::operator A &() [user-defined], A & -> A [rvalue-to-lvalue]

では、標準の変換シーケンスをどのようにランク付けしますか?確認する場所は、13.3.3.1.1の表12です。これは、左辺値から右辺値への変換がID変換と同じランク(「完全一致」)であることを指定しています。したがって、2つのユーザー定義の変換シーケンスを区別できず、プログラムの形式が正しくありません。


サイドバー

ユーザー定義の変換シーケンスのランク付けに関して、13.3.3と13.3.3.2の違いは何ですか?

13.3.3を使用すると、コンパイラはさまざまなユーザー定義の変換演算子を区別できます。13.3.3.2を使用すると、コンパイラは、引数にユーザー定義の変換が必要なさまざまな関数を区別できます。

だから、コードで

struct A {
    operator int();
    operator float();
} a;
void f(int);
f(a);

13.3.3が適用され、A::operator int()上で選択されA::operator float()ます。コード内

struct A {
    operator int();
} a;
void f(int);
void f(double);
f(a);

13.3.3.2が適用され、void f(int)で選択されvoid f(double)ます。ただし、コードでは

struct A {
    operator int();
    operator float();
} a;
void f(int);
void f(double);
f(a);

13.3.3は何度も何度も優先し、13.3.3.2は何A::operator int() -> void f(int)A::operator float() -> void f(int)も何度も優先しますが、と変換シーケンスを区別する方法はありません(同じユーザー定義の変換演算子も同じオーバーロードも含まれていないため)。そのため、コードの形式が正しくありません。float -> doubleint -> doubleint -> intint -> doublefloat -> doublefloat -> intint -> intfloat -> doublef

于 2012-07-18T15:59:11.213 に答える