13

我有一些如下代码:

class bar;

class foo
{
public:
    operator bar() const;
};

class bar
{
public:
    bar(const foo& foo);
};

void baz() {
    foo f;
    bar b = f;   // [1]

    const foo f2;
    bar b2 = f2; // [2]
}

GCC 在 [2] 而不是 [1] 处给出错误。Clang 两者都出错,显然 MSVC 两者都没有出错。谁是对的?

4

1 回答 1

2

tl;博士

模糊的。(另外,如果你停在 tl;dr,那么language-lawyer标签可能不是你的那杯茶。^_^)

剧透

两个候选者都有一个const foo&参数,它同样绑定到一个const fooorfoo参数。没有其他规则会偏爱一个或另一个功能。


根据当前的 C++ 工作草案对其进行分解

初始化程序 [dcl.init]

在这两种情况下

通过用户定义的转换复制初始化类[over.match.copy]

T是被初始化的类型,在这两种情况下都是bar. S是初始化表达式的类型,在这两种情况下fooconst foo分别。

  • 转换构造函数T是候选([over.match.copy]/1.1
    • bar::bar(const foo& foo);是候选人
  • 初始化表达式的类型是_cv_ S这样考虑非显式转换函数:( [over.match.copy]/1.2 )
    • foo::operator bar() const不隐藏在 内foo或 内const foo,并产生bar与 相同的结果T,因此是候选。

所以我们的候选名单在这两种情况下都是一样的:

  • bar::bar(const foo& foo)
  • foo::operator bar() const

在这两种情况下,我们都有一个用户定义的转换,包括:

  1. 源类型到用户定义的转换参数的标准转换
  2. 用户定义的转换(上述两个函数之一)到结果类型
  3. 结果类型到目标类型的标准转换

如果我们选择构造函数,“结果类型”是“其结果对象由构造函数初始化的目标类型的 cv 非限定版本的纯右值”([dcl.init]/17.6.3),因此对于两个候选函数,第二个标准转换是Identity ( bar-> bar)。

重载分辨率[over.match]

子集可行的候选函数[over.match.viable]

根据[dcl.init]/17.6.3,初始化表达式将分别作为所选调用的参数,在这两种情况fooconst foo

bar::bar(const foo& foo)

foo::operator bar() const

选择最佳可行函数[over.best.ics]

两者都是Identity => User Defined Conversion => Identity,即是用户定义的转换序列

在.ics.rank 上对转换序列进行排名

我们可以在序列之间建立排名吗?仅在以下情况之一适用时

转换序列是无法区分的,即既不是更好也不是更差

最佳可行函数over.match.best

任何一个功能都是“更好”的功能吗?仅在以下情况之一适用时

也不是一个“更好”的函数,所以调用是 ill-formed[over.match.best]/2

于 2017-08-14T13:00:57.400 回答