Reputation: 21047
In following program, struct C
has two constructors : one from std::initializer_list<A>
and the other from std::initializer_list<B>
. Then an object of the struct is created with C{{1}}
:
#include <initializer_list>
struct A {
int i;
};
struct B {
constexpr explicit B(int) {}
};
struct C {
int v;
constexpr C(std::initializer_list<A>) : v(1) {}
constexpr C(std::initializer_list<B>) : v(2) {}
};
static_assert( C{{1}}.v == 1 );
Since only aggregate A
can be implicitly constructed from int
, one could expect that C(std::initializer_list<A>)
is preferred and the program succeeds. And indeed it does in Clang.
However GCC complains:
error: call of overloaded 'C(<brace-enclosed initializer list>)' is ambiguous
note: candidate: 'constexpr C::C(std::initializer_list<B>)'
note: candidate: 'constexpr C::C(std::initializer_list<A>)'
and so does MSVC:
error C2440: '<function-style-cast>': cannot convert from 'initializer list' to 'C'
note: No constructor could take the source type, or constructor overload resolution was ambiguous
Demo: https://gcc.godbolt.org/z/joz91q4ed
Which compiler is correct here?
Upvotes: 9
Views: 195
Reputation: 39758
The wording could be clearer (which is unsurprising), but GCC and MSVC are correct here: the relevant rule ([over.ics.list]/7) checks only that
overload resolution […] chooses a single best constructor […] to perform the initialization of an object of type
X
from the argument initializer list
so the fact that the initialization of B
from {1}
would be ill-formed is irrelevant.
There are several such places where implicit conversion sequences are more liberal than actual initialization, causing certain cases to be ambiguous even though some of the possibilities wouldn’t actually work. If the programmer was confused and thought one of those near misses was actually a better match, it’s a feature that the ambiguity is reported.
Upvotes: 8