Bug 83264 (Cwg1467) - std::initializer_list with a single element selects the wrong overload
Summary: std::initializer_list with a single element selects the wrong overload
Status: UNCONFIRMED
Alias: Cwg1467
Product: gcc
Classification: Unclassified
Component: c++ (show other bugs)
Version: 7.2.1
: P3 normal
Target Milestone: ---
Assignee: Jason Merrill
URL:
Keywords: wrong-code
: 99273 105683 109523 (view as bug list)
Depends on:
Blocks:
 
Reported: 2017-12-03 20:22 UTC by Ruslan Altynbaev
Modified: 2024-03-19 18:30 UTC (History)
6 users (show)

See Also:
Host:
Target:
Build:
Known to work:
Known to fail: 11.2.0, 4.4.7, 4.7.4, 4.9.4
Last reconfirmed: 2021-12-17 00:00:00


Attachments
patch to follow other implementations (1.45 KB, patch)
2022-12-12 19:24 UTC, Jason Merrill
Details | Diff

Note You need to log in before you can comment on or make changes to this bug.
Description Ruslan Altynbaev 2017-12-03 20:22:19 UTC
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.
Comment 1 Andrew Pinski 2021-12-17 11:59:53 UTC
Confirmed.
Comment 2 Jonathan Wakely 2021-12-17 13:48:38 UTC
Maybe another case of DR 2137 (PR 85577)
Comment 3 Andrew Pinski 2021-12-17 14:04:46 UTC
DR 1467 describes that gcc is correct.
Comment 4 Andrew Pinski 2021-12-17 14:05:24 UTC
https://wg21.link/cwg1467
Comment 5 Ruslan Altynbaev 2021-12-17 16:15:45 UTC
Actually DR 2137 describes that:

https://wg21.link/cwg2137
Comment 6 Jonathan Wakely 2021-12-17 17:05:17 UTC
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!
Comment 7 Andrew Pinski 2021-12-17 20:37:19 UTC
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.
Comment 8 Andrew Pinski 2022-02-04 22:28:39 UTC
*** Bug 99273 has been marked as a duplicate of this bug. ***
Comment 9 Fedor Chelnokov 2022-02-05 08:43:41 UTC
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
Comment 10 Jonathan Wakely 2022-05-23 10:52:58 UTC
*** Bug 105683 has been marked as a duplicate of this bug. ***
Comment 11 Jason Merrill 2022-12-12 19:22:55 UTC
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.
Comment 12 Jason Merrill 2022-12-12 19:24:04 UTC
Created attachment 54076 [details]
patch to follow other implementations

Here's the patch if we do want to go that way.
Comment 13 Andrew Pinski 2023-04-15 09:35:22 UTC
*** Bug 109523 has been marked as a duplicate of this bug. ***
Comment 14 Jason Merrill 2023-12-20 16:21:58 UTC
Pinged CWG again.