Consider the following: #include <iostream> #include <initializer_list> using namespace std; template<typename T> struct A { A(int) { cout << "A(int)\n"; } A(std::initializer_list<T>) { cout << "A(initializer_list)\n"; } }; struct UdfInt { UdfInt(int) {} }; int main() { A<UdfInt> obj({ 10 }); return 0; } GCC (any version) prints "A(initializer_list)". Other compilers (clang, MSVC) print "A(int)". According to [dcl.init.list]/3.9, a single-element braced-init-list should be unwrapped which means the A(int) overload is the best viable alternative since the other one needs a user-defined conversion.
Confirmed.
Maybe another case of DR 2137 (PR 85577)
DR 1467 describes that gcc is correct.
https://wg21.link/cwg1467
Actually DR 2137 describes that: https://wg21.link/cwg2137
I don't know what the "right" behaviour is. I don't think any compiler except GCC actually implements 2137, as it causes problems. I'm not even sure if this is a case where 2137 applies!
The example at the end of the change of DR 1467: Example: void f1(int); // #1 void f1(std::initializer_list<long>); // #2 void g1() { f1({42}); } // chooses #2 void f2(std::pair<const char*, const char*>); // #3 void f2(std::initializer_list<std::string>); // #4 void g2() { f2({"foo","bar"}); } // chooses #4 —end example] http://www.open-std.org/cwg1589 is also this one too. Plus http://www.open-std.org/cwg2076 Note I don't think constructor of A matter, it could be a function instead, e.g.: #include <iostream> #include <initializer_list> using namespace std; struct UdfInt { UdfInt(int) {} }; void f(int) { cout << "f(int)\n"; } void f(UdfInt) { cout << "f(initializer_list)\n"; } void f(std::initializer_list<UdfInt>) { cout << "f(initializer_list)\n"; } int main() { f({10}); return 0; } GCC prints f(initializer_list) while all the rest of the compilers print A(int). The big question initializer_list<UdfInt> a better conversion from initializer_list<int> than int. I think the answer is yes. The example in DR 1467 shows that the conversion from initializer_list<int> to initializer_list<long> is better than the conversion from initializer_list<int> to int (clang implements that, ICC and MSVC do not). I think the language change in end of DR 1467 is what applies here the most: even if one of the other rules in this paragraph would otherwise apply. Also if we take: #include <iostream> #include <initializer_list> #include <string> using namespace std; #if 0 static void f(const char*) { cout << "f(const char*)\n"; } #endif static void f(std::string) { cout << "f(std::string)\n"; } static void f(std::initializer_list<std::string>) { cout << "f(initializer_list)\n"; } int main() { f({"10"}); return 0; } Right now all compilers agree f(initializer_list) but once we add in f(const char*), GCC still selects f(initializer_list) while ICC/MSVC/clang select f(const char*). That seems a bit odd.
*** Bug 99273 has been marked as a duplicate of this bug. ***
There is a related discussion: https://stackoverflow.com/a/47618530/7325599 And it is noted there that according to [over.ics.rank]/2 just before [over.ics.rank]/3: — a standard conversion sequence is a better conversion sequence than a user-defined conversion
*** Bug 105683 has been marked as a duplicate of this bug. ***
I agree with comment #7 that this is DR1467, not 2137. Both deal with the relative priority of single-element initialization and initializer_list initialization; 1467 is overload resolution between initializer_list and something else, 2137 is at a higher level determining how to initialize a class from a braced-init-list. In this testcase the extra () mean that we aren't dealing with list-initialization of obj, but rather of the constructor parameter, so 1467 is the relevant DR. This is also the same issue as PR64665, where a 2015 comment from Richard Smith of Clang agrees with my understanding at the time that the initializer_list tiebreaker takes precedence over everything else. This is consistent with the effect of 2137--which no other compiler seems to implement yet. However, I now see that this isn't what the standard actually says: as comment #9 points out, [over.ics.rank] talks about comparing forms first, and the initializer_list tiebreaker only applies to conversions of the same form. I thought of list-initialization as its own form, but the standard doesn't say that; the three forms are still standard, user-defined, or ellipsis. I've asked the CWG reflector for input.
Created attachment 54076 [details] patch to follow other implementations Here's the patch if we do want to go that way.
*** Bug 109523 has been marked as a duplicate of this bug. ***
Pinged CWG again.