来自 initalizer_list 和需要转换的值的构造函数之间的重载解析,编译器不同
Overload resolution between constructors from initalizer_list and from a value requiring conversion, compilers diverge
在下面的代码中,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 );
事实证明,GCC 和 Clang 在这里更喜欢来自 initializer_list
的构造函数(尽管 int
参数必须在 char
中转换)并且整个程序都被接受,但是 MSVC选择 A(int)
构造函数,使 static_assert
失败。演示:https://gcc.godbolt.org/z/qx7W417Mv
哪个编译器就在这里?
Clang 和 GCC 是对的,MSVC 有一个错误
这是CWG 1589:
1589. Ambiguous ranking of list-initialization sequences
Section: 16.3.3.2 [over.ics.rank]
Status: CD4
Submitter: Johannes Schaub
Date: 2012-11-21
[Moved to DR at the November, 2014 meeting.]
The interpretation of the following example is unclear in the current
wording:
void f(long);
void f(initializer_list<int>);
int main() { f({1L});
The problem is that a list-initialization sequence can also be a
standard conversion sequence, depending on the types of the elements
and the type of the parameter, so more than one bullet in the list
in 16.3.3.2 [over.ics.rank] paragraph 3 applies:
[...]
这些项目符号给出了与上面的例子相反的结果,并且有
是选择的实现方差。
[...]
提议的决议(2014 年 6 月):
此问题已通过问题解决方案得到解决
1467.
根据决议已通过 CWG 1467:
解决
1467. List-initialization of aggregate from same-type object
[...]
Proposed resolution (June, 2014):
[...]
- Move the final bullet of 16.3.3.2 [over.ics.rank] paragraph 3 to the beginning of the list and change it as follows:
[...]
even if one of the other rules in this paragraph would otherwise apply. [Example: ... — end example]
This resolution also resolves issues 1490, 1589, 1631, 1756, and 1758.
特别是 re-ording [over.ics.rank]/3 such that [over.ics.rank]/3.1 now applies for the OP's case, and takes precedence over the other rules of [over.ics.rank]/3:
List-initialization sequence L1
is a better conversion sequence than
list-initialization sequence L2
if
- (3.1.1)
L1
converts to std::initializer_list<X>
for some X
and L2
does not, or, if not that
- (3.1.2) [...]
even if one of the other rules in this paragraph would otherwise apply.
GCC 和 Clang 等编译器从 back-ported DR 1467 开始符合早期标准(C++11 和 C++14); Clang 特别是 as of Clang 3.7, and we can verify the CWG 1589 connection to OP's example as it is rejected by Clang 3.6 but accepted by Clang 3.7 (DEMO).
另一方面,MSVC 似乎没有实现此更改,即使根据更新的 C++17 标准,它不再“只是一个”DR。
在下面的代码中,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 );
事实证明,GCC 和 Clang 在这里更喜欢来自 initializer_list
的构造函数(尽管 int
参数必须在 char
中转换)并且整个程序都被接受,但是 MSVC选择 A(int)
构造函数,使 static_assert
失败。演示:https://gcc.godbolt.org/z/qx7W417Mv
哪个编译器就在这里?
Clang 和 GCC 是对的,MSVC 有一个错误
这是CWG 1589:
1589. Ambiguous ranking of list-initialization sequences
Section: 16.3.3.2 [over.ics.rank]
Status: CD4
Submitter: Johannes Schaub
Date: 2012-11-21[Moved to DR at the November, 2014 meeting.]
The interpretation of the following example is unclear in the current wording:
void f(long); void f(initializer_list<int>); int main() { f({1L});
The problem is that a list-initialization sequence can also be a standard conversion sequence, depending on the types of the elements and the type of the parameter, so more than one bullet in the list in 16.3.3.2 [over.ics.rank] paragraph 3 applies:
[...]
这些项目符号给出了与上面的例子相反的结果,并且有 是选择的实现方差。
[...]
提议的决议(2014 年 6 月):
此问题已通过问题解决方案得到解决 1467.
根据决议已通过 CWG 1467:
解决1467. List-initialization of aggregate from same-type object
[...]
Proposed resolution (June, 2014):
[...]
- Move the final bullet of 16.3.3.2 [over.ics.rank] paragraph 3 to the beginning of the list and change it as follows:
[...]
even if one of the other rules in this paragraph would otherwise apply. [Example: ... — end example]
This resolution also resolves issues 1490, 1589, 1631, 1756, and 1758.
特别是 re-ording [over.ics.rank]/3 such that [over.ics.rank]/3.1 now applies for the OP's case, and takes precedence over the other rules of [over.ics.rank]/3:
List-initialization sequence
L1
is a better conversion sequence than list-initialization sequenceL2
if
- (3.1.1)
L1
converts tostd::initializer_list<X>
for someX
andL2
does not, or, if not that- (3.1.2) [...]
even if one of the other rules in this paragraph would otherwise apply.
GCC 和 Clang 等编译器从 back-ported DR 1467 开始符合早期标准(C++11 和 C++14); Clang 特别是 as of Clang 3.7, and we can verify the CWG 1589 connection to OP's example as it is rejected by Clang 3.6 but accepted by Clang 3.7 (DEMO).
另一方面,MSVC 似乎没有实现此更改,即使根据更新的 C++17 标准,它不再“只是一个”DR。