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 对隐式转换序列进行排名[over.ics.rank]

3 - 两个相同形式的隐式转换序列是不可区分的转换序列,除非以下规则之一适用:[...]

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

据我了解,13.3.3 允许编译器区分不同的用户定义的转换运算符,而 13.3.3.2 允许编译器区分不同的函数(某些函数的重载f),每个函数都需要在其参数中进行用户定义的转换(请参阅我的侧边栏给定以下代码(在 GCC 4.3 中),为什么要调用两次引用转换?)。

是否有其他规则可以区分用户定义的转换序列?https://stackoverflow.com/a/1384044/567292的答案表明 13.3.3.2:3 可以根据隐式对象参数(到转换运算符)的 cv 限定或构造函数或聚合初始化的单个非默认参数,但我看不出这有什么关系,因为这需要在相应用户定义的转换序列的第一个标准转换序列之间进行比较,而标准没有t 似乎没有提及。

假设 S1 优于 S2,其中 S1 是 U1 的第一个标准转换序列,S2 是 U2 的第一个标准转换序列,是否可以得出 U1 优于 U2?换句话说,这段代码格式正确吗?

struct A {
    operator int();
    operator char() const;
} a;
void foo(double);
int main() {
    foo(a);
}

g++ (4.5.1)、Clang (3.0) 和Comeau (4.3.10.1) 接受它,更喜欢 non-const-qualified A::operator int(),但我希望它会因为模棱两可而被拒绝,因此格式不正确。这是标准的缺陷还是我对它的理解?

4

1 回答 1

6

这里的技巧是,从类类型转换为非类类型实际上并没有将任何用户定义的转换列为隐式转换序列。

struct A {
    operator int();
    operator char() const;
} a;
void foo(double);
int main() {
    foo(a);
}

在表达式foo(a)中,foo显然命名了一个非重载的非成员函数。double该调用需要使用单个表达式复制初始化 (8.5p14) 类型的函数参数,该表达式a是类类型的左值A

由于目标类型double不是 cv 限定的类类型,但源类型A 是,候选函数由第 13.3.1.5 节定义,其中S=AT=doubleA只考虑类和任何基类中的转换函数A。如果满足以下条件,则转换函数位于候选集中:

  • 它没有隐藏在类A中,并且
  • 它不是explicit(因为上下文是复制初始化),并且
  • 标准转换序列可以将函数的返回类型(不包括任何引用限定符)转换为目标类型double

好的,两个转换函数都符合条件,所以候选函数是

A::operator int();        // F1
A::operator char() const; // F2

使用 13.3.1p4 中的规则,每个函数都将隐式对象参数作为其参数列表中的唯一内容。 F1的参数列表是“(对 的左值引用A)”,而F2的参数列表是“(对 的左值引用const A)”。

接下来我们检查这些功能是否可行(13.3.2)。每个函数的参数列表中都有一种类型,并且只有一个参数。每个参数/参数对是否有隐式转换序列?当然:

  • ICS1(F1):将隐式对象参数(类型的左值引用A)绑定到表达式a(类型的左值A)。
  • ICS1(F2):将隐式对象参数(类型的左值引用const A)绑定到表达式a(类型的左值A)。

由于没有进行派生到基础的转换,因此这些引用绑定被视为身份转换 (13.3.3.1.4p1) 的特殊情况。是的,这两个功能都是可行的。

现在我们必须确定一个隐式转换序列是否比另一个更好。这属于 13.3.3.2p3 中大列表中的第五个子项:除了顶级 cv-qualifiers 之外,两者都是对同一类型的引用绑定。由于 的引用类型ICS1(F2)比 的引用类型更具有 cv 限定ICS1(F1)ICS1(F1)因此优于ICS1(F2).

因此F1, 或A::operator int(), 是最可行的函数。甚至没有用户定义的转换(严格定义由SCS +(转换构造函数或转换函数)+ SCS组成的ICS类型)进行比较。

现在,如果foo重载,则a需要比较参数上的用户定义转换。因此,用户定义的转换(identity ++ A::operator int()to intdouble将与其他隐式转换序列进行比较。

于 2012-07-25T02:21:46.077 回答