为什么在初始化期间应用了用户定义的转换?

5 c++ language-lawyer implicit-conversion c++11

以下代码工作正常:

#include <iostream>

struct B
{
    operator int()
    {
        return int();
    }
};

struct A
{
    A(int, int){ std::cout << "A(int, int)" << std::endl; }
};

A a({B(), B()});

int main()
{ 
}
Run Code Online (Sandbox Code Playgroud)

并产生输出:

A(int, int)
Run Code Online (Sandbox Code Playgroud)

DEMO

但我不明白为什么?标准说的是:

但是,当考虑构造函数或用户定义的转换函数的参数时,如果在类复制初始化的第二步中复制/移动临时函数时,通过13.3.1.3调用该函数,则通过13.3.1.7传递时初始化程序列表作为单个参数,或者当初始化程序列表只有一个元素并且转换为某个类X或引用(可能是cv限定的)时,X仅被视为X [...]的构造函数的第一个参数考虑标准转换序列和省略号转换序列

所以在我们的例子中,我们考虑了构造函数的参数(它是{B(), B()}).更确切地说,我们将initializer-list作为单个参数传递(我引用的规则中的第二个案例).现在,我们需要将initializer-list的第一个元素(临时类型B)转换为int,并且唯一的方法是应用用户定义的convertion(B::operator int()).但是,正如在规则末尾所说的那样,我引用标准转换序列和省略号转换序列.因为,该代码不起作用,它应该抛出A(int, int)不可行或类似的错误.

怎么了.可能是一个错误?

Col*_*mbo 1

该措辞有缺陷,并随 C++14 进行了更改。现在[over.best.ics]/4 阅读

\n\n
\n

然而,如果目标是

\n\n
    \n
  • 构造函数的第一个参数或
  • \n
  • [\xe2\x80\xa6]
  • \n
\n\n

并且构造函数或用户定义的转换函数是候选\n

\n\n
    \n
  • 13.3.1.3,当参数是类复制初始化第二步中的临时参数时
  • \n
  • 13.3.1.4、13.3.1.5 或 13.3.1.6(所有情况),或
  • \n
  • 13.3.1.7 的第二阶段,当初始值设定项列表只有一个元素,并且目标是 class 的 a\n 构造函数的第一个参数X,并且转换为X或引用\n (可能是 cv 限定的)X
  • \n
\n\n

不考虑用户定义的转换序列。[注意:这些规则可防止在重载决策期间应用多个用户定义的转换,从而避免无限递归。\xe2\x80\x94\n 尾注]

\n
\n\n

B()to的转换int不在此范围内 - 粗体短语仅适用于在复制初始化期间将引用绑定到临时对象。
然而, Clang根据上面的内容拒绝了这个示例代码:

\n\n
class A;\n\nstruct B\n{\n    operator A();\n};\n\nstruct A\n{\n    A(A const&){}\n};\n\nA a{B()};\n
Run Code Online (Sandbox Code Playgroud)\n