来自 initalizer_list 的构造函数和来自需要转换的值的构造函数之间的重载解析,编译器存在分歧

Fed*_*dor 5 c++ initializer-list language-lawyer overload-resolution

在下面的代码中,struct A有两个构造函数:A(int)A(std::initializer_list<char>)。然后使用以下命令创建该结构的对象A({0})

#include <initializer_list>

struct A {
    int a;
    constexpr A(int) { a = 1; } 
    constexpr A(std::initializer_list<char>) { a = 2; }
};

static_assert( A({0}).a == 2 );
Run Code Online (Sandbox Code Playgroud)

事实证明,GCC 和 Clang 更喜欢这里的构造函数initializer_list(尽管int必须在 中转换参数char)并且整个程序被接受,但 MSVC 选择A(int)构造函数,导致static_assert. 演示: https: //gcc.godbolt.org/z/qx7W417Mv

这里是哪个编译器?

dfr*_*fri 7

Clang 和 GCC 是对的,MSVC 有 bug

\n

这是CWG 1589

\n
\n

1589. 列表初始化序列的排序不明确

\n

部分: 16.3.3.2 [over.ics.rank]
\n状态:CD4
\n提交者:Johannes Schaub
\n日期:2012-11-21

\n

[在 2014 年 11 月的会议上移至 DR。]

\n

在当前的措辞中,以下示例的解释不清楚:

\n
void f(long);\nvoid f(initializer_list<int>);\nint main() { f({1L});\n
Run Code Online (Sandbox Code Playgroud)\n

问题是列表初始化序列也可以是标准转换序列,具体取决于元素的类型和参数的类型,因此列表中的项目符号不只一个, \n16.3.3.2 [over.ics .rank] 第 3 段适用

\n
\n

[...]

\n
\n

这些项目符号为上面的示例提供了相反的结果,并且存在\n选择的实现差异。

\n

[...]

\n

拟议决议(2014 年 6 月):

\n

此问题已通过问题\n 1467的解决方案得到解决。

\n
\n

根据该决议,该决议已通过CWG 1467解决:

\n
\n

1467. 来自相同类型对象的聚合的列表初始化

\n

[...]

\n

拟议决议(2014 年 6 月):

\n

[...]

\n
    \n
  1. 将 16.3.3.2 [over.ics.rank] 第 3 段的最后一个项目符号移至列表的开头,并将其更改如下:
  2. \n
\n

[...]

\n

即使本款中的其他规则之一将适用。[示例: ... \xe2\x80\x94结束示例]

\n

该决议还解决了问题 1490、1589、1631、1756 和 1758。

\n
\n

特别是通过重新排序[over.ics.rank]/3,使得[over.ics.rank]/3.1现在适用于 OP 的情况,并优先于[over.ics.rank的其他规则]/3

\n
\n

列表初始化序列L1是比\n列表初始化序列更好的转换序列,L2 如果

\n
    \n
  • (3.1.1)L1转换为std\xe2\x80\x8b::\xe2\x80\x8binitializer_\xc2\xadlist<X>对于某些X 且不L2或者,如果不是
  • \n
  • (3.1.2) [...]
  • \n
\n

即使本款中的其他规则之一将适用

\n
\n

GCC 和 Clang 等编译器已将 DR 1467 向后移植到早期标准(C++11 和 C++14);具体从 Clang 3.7 开始,我们可以验证 CWG 1589 与 OP 示例的连接,因为它被 Clang 3.6 拒绝,但被 Clang 3.7 接受(DEMO)。

\n

另一方面,MSVC 似乎尚未实现此更改,即使根据更新的 C++17 标准,它不再“只是一个”DR。

\n