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 ,它的格式不正确)。

左值到右值转换与恒等转换具有相同的等级,因此(根据 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 - 如果恰好有一个可行函数比所有其他可行函数更好,那么它就是重载决议选择的那个;否则调用格式不正确。

定义本身很复杂,但用户定义的转换有两点需要注意:

首先,将用户定义转换作为转换序列的应用程序指定为分解为一S_a - U - S_b​​个标准转换序列的序列,然后是一个用户定义的转换,然后是另一个标准转换序列。这涵盖了所有情况;一个转换序列中不能有多个用户定义的转换,标准转换序列可以是“身份转换”,即不需要转换。

其次,在比较用户定义的转换序列时,唯一重要的部分是第二个标准转换序列。这是在 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 - 两个相同形式的隐式转换序列是不可区分的转换序列,除非以下规则之一适用:[...]

  • 如果用户定义的转换序列 U1 包含相同的用户定义的转换函数或构造函数或聚合初始化,并且 U1 的第二个标准转换序列优于第二个标准转换序列,则用户定义的转换序列 U1 是比另一个用户定义的转换序列 U2 更好的转换序列U2 的。

因此,在比较转换序列时U1 = (S1_a - U'1 - S1_b)U2 = (S2_a - U'2 - S2_b)唯一重要的是 和 的相对S1_b等级S2_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,它指定左值到右值的转换与恒等转换具有相同的等级(“精确匹配”)。所以无法区分这两个用户自定义的转换序列,程序是病态的。


侧边栏

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.3A::operator int() -> void f(int)一遍A::operator float() -> void f(int)又一float -> double遍地int -> double喜欢,而 13.3.3.2 一遍又一遍地喜欢int -> intint -> doublefloat -> double无法float -> int区分 theint -> intfloat -> double转换序列(因为它们既不包含相同的用户定义的转换运算符也不包含相同的重载f),并且所以代码格式不正确。

于 2012-07-18T15:59:11.213 回答