5

So I am aware that braces in code can mean more than just an initializer_list: What Is a Curly-Brace Enclosed List If Not an intializer_list?

But what should they default to?

For example, say that I define an overloaded function:

void foo(const initializer_list<int>& row_vector) { cout << size(row_vector) << "x1 - FIRST\n"; }
void foo(const initializer_list<initializer_list<int>>& matrix) { cout << size(matrix) << 'x' << size(*begin(matrix)) << " - SECOND\n"; }

If I call foo({ 1, 2, 3 }) the 1st will obviously be called. And if I call foo({ { 1, 2, 3 }, { 4, 5, 6 }, { 7, 8, 9 } }) the 2nd will obviously be called.

But what if I call:

foo({ { 1 }, { 2 }, { 3 } })

Are those nested braces int-initializers or initializer_list<int> intializers? gcc says it's ambiguous But if you take that code and run it on http://webcompiler.cloudapp.net/ Visual Studio says it's just constructing an initializer_list<int>. Who's right? Should there be a default here?

Community
  • 1
  • 1
Jonathan Mee
  • 37,899
  • 23
  • 129
  • 288
  • 2
    It's ambiguous, because I don't know which is right. If a human can't know, what hope could a compiler have? – Mooing Duck Aug 05 '16 at 18:06
  • 3
    Both `int i = {42};` and `std::initializer_list ini = { 42 };` are correct [Demo](http://coliru.stacked-crooked.com/a/704c8ce1864b1c3e). So it is ambiguous. – Jarod42 Aug 05 '16 at 18:11
  • @MooingDuck This is probably my misconception, but apart from list initialization I thought we had to specify what type we were initializing. Not just do `{ 1 }`, I thought we had to do `int{ 1 }`. If that's the case Visual Studio seems to make the most sense. – Jonathan Mee Aug 05 '16 at 18:11
  • 1
    @JonathanMee: That's not the case, you don't have to specify the type. `{{1}}` can match `init_list`, or `init_list>`, [or even `std::array`](http://coliru.stacked-crooked.com/a/fa558cf25155c0e1), depending on the context. – Mooing Duck Aug 05 '16 at 18:19
  • @MooingDuck `cout << { 1 } << endl` is illegal for example. Even though there is an insertion operator for `int`s. in my mind if you haven't directly specified the type it shouldn't be a constructor. Obviously initialization does that and obviously `int{ 1 }` does that, but `{ 1 }` doesn't in my mind. I think of that as a `initializer_list`. – Jonathan Mee Aug 05 '16 at 18:28
  • 4
    That's illegal not because there isn't a conversion, but because the grammar prevents you from writing a braced-init-list there. – T.C. Aug 05 '16 at 18:48
  • @T.C. That really helps me clarify. So you're saying that anywhere I would be able to initialize an `intializer_list` I would be equally eligible to initialize an `int`, and since there is no preferred conversion this is ambiguous. – Jonathan Mee Aug 05 '16 at 19:33
  • 1
    @Jonathan No, and that isn't true. – Barry Aug 05 '16 at 22:39

1 Answers1

6

The rule is in [over.ics.list]:

Otherwise, if the parameter type is std::initializer_list<X> and all the elements of the initializer list can be implicitly converted to X, the implicit conversion sequence is the worst conversion necessary to convert an element of the list to X, or if the initializer list has no elements, the identity conversion.

In both your overloads, the worst conversion necessary is the identity conversion. So we have two implicit conversion sequences with rank identity. There is a rule in [over.match.best] which prefers a list-initialization of a std::initializer_list<X> over alternatives (so std::initializer_list<int> is preferred to int for {1}), but there nothing to suggest that this rule should apply recursively.

Since there's nothing to disambiguate the two conversion sequences, the call is ambiguous. gcc and clang are correct to reject.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • I'm kinda curious about whether MS decided that as it doesn't specify whether the rule applies recursively, it would be safest to treat it as such unless and until it was otherwise stated, or if they just messed something up. – Justin Time - Reinstate Monica Aug 05 '16 at 20:05
  • I think this warrants a CWG issue, as applying that rule recursively seems more useful. – bogdan Aug 05 '16 at 23:13
  • 1
    @bogdan I [posted](https://groups.google.com/a/isocpp.org/forum/#!topic/std-discussion/2VUQYjJ-8Bk) about it. People seem justifiably more interested in `enable_shared_from_this` :) – Barry Aug 09 '16 at 13:38