62

I love auto in C++11. It's wonderful. But it has one inconsistency that really gets on my nerves, because I trip over it all the time:

int i = 3;       // i is an int with value 3
int i = int{3};  // i is an int with value 3
int i(3);        // i is an int with value 3 (possibly narrowing, not in this case)
int i{3};        // i is an int with value 3

auto i = 3;      // i is an int with value 3
auto i = int{3}; // i is an int with value 3
auto i(3);       // i is an int with value 3
auto i{3};       // wtf, i is a std::initializer_list<int>?!

This strange behaviour is confusing for newcomers, and annoying for experienced users -- C++ has enough little inconsistencies and corner cases that one has to keep in mind as it is. Can anybody explain why standards committee decided to introduce a new one in this case?

I could understand it if declaring a variable of type std::initializer_list was something that was useful or done frequently, but in my experience it's almost never deliberate -- and in the rare cases where you did want to do it, any of

std::initializer_list<int> l{3};
auto l = std::initializer_list<int>{3};
auto l = {3}; // No need to specify the type

would work just fine. So what's the reason behind the special case for auto x{i}?

Tristan Brindle
  • 16,281
  • 4
  • 39
  • 82
  • 18
    That's pretty much why the [rule is going to change](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3922.html). – T.C. Sep 01 '14 at 20:02
  • 1
    Related: http://stackoverflow.com/q/17582667 – dyp Sep 01 '14 at 20:02
  • 1
    Solution: don't use auto for any of those cases (except #2). :-) – dlf Sep 01 '14 at 20:03
  • 5
    http://scottmeyers.blogspot.com/2014/03/if-braced-initializers-have-no-type-why.html – Piotr Skotnicki Sep 01 '14 at 20:05
  • 11
    if you're asking "why C++ committee have done a wrong design choice, which they are now trying to undo", well, frankly, the answer is "*because they are human beings and as such they sometimes may do things that are wrong or controversial*"?" I honestly guess that the only real way to get a definite answer would be to ask one of the members of the committee directly. –  Sep 01 '14 at 20:11

1 Answers1

51

To make long story short:

  • a braced initializer expression {} has no type by itself
  • auto has to infer type information
  • int{3} obviously means "create an int var with value taken from initializer list", thus its type is just int and can be used in any wider context (int i = int{3} will work and auto i = int{3} can deduce type, because right side is obviously of type int)
  • {3} by itself has no type (it can't be int, because it's not a value but an initializer list), so auto wouldn't work — but, because committee considered that auto should still work in this case, they decided that the "best" type for (yeah, typeless by definition) initializer list would be... std::initializer_list, as you already probably guessed.

But, as you pointed out, this made the whole behaviour of auto quite semantically inconsistent. That's why there were proposals to change it — namely N3681, N3912 and N3922 — submitted to the committee. Former proposal was REJECTED as FI3 due to no committee consensus on this matter, http://isocpp.org/files/papers/n3852.html#FI3 , current (N3922) got adopted ca. Q1 of 2015;

tl;dr you may assume that standards-compliant compilers1 with bleeding-edge C++ support2 either have the new, more sane-ish semantics already in place, or will have it shortly.

The Standardization Committee acknowledged the problem by adopting N3922 into draft C++17.

— so it's

auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list<int>
auto x2 = { 1, 2.0 }; // error: cannot deduce element type
auto x3{ 1, 2 }; // error: not a single element
auto x4 = { 3 }; // decltype(x4) is std::initializer_list<int>
auto x5{ 3 }; // decltype(x5) is int

now, for better or worse.

further reading:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3681.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3912.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3922.html

http://scottmeyers.blogspot.com/2014/03/if-braced-initializers-have-no-type-why.html

http://herbsutter.com/2014/11/24/updates-to-my-trip-report/


1GCC 5.1 (& up) apparently uses N3922 even in C++11/C++14 mode

2Clang 3.8, with the caveat

This is a backwards-incompatible change that is applied to all language versions that allow type deduction from auto (per the request of the C++ committee).

Community
  • 1
  • 1
  • 3
    N3922 was adopted more than a year ago (November 2014, in Urbana). – T.C. Feb 19 '16 at 15:06
  • @T.C. many thanks for spotting that one - and strange that no one mentioned it earlier! –  Feb 19 '16 at 16:52
  • @vaxquis Do you guys know when this will be accepted and applied to most compilers like g++ and clang? – Curious Feb 21 '16 at 22:54
  • @Curious browsing the official compiler docs or using google should answer this question for you better than I can. Still, I've updated the answer accordingly. –  Feb 22 '16 at 14:14
  • @vaxquis Why does X3 (direct initialized) is an error and X1 (copy initialized) is acceptable? – gedamial Jun 03 '16 at 18:12
  • 1
    @gedamial see the linked articles for explanation. note that `x1` is acceptable because RH is visibly `std::initializer_list` - OTOH, direct initialization of a primitive requires a single `{value}` parameter, so with `{value1,value2}` it makes little sense. –  Jun 03 '16 at 20:09
  • 1
    Confusingly, current GCC and Clang reject `auto i{1,2};` even in C++11 mode, though it is valid in this standard version, and is accepted by e.g. GCC 4.8 (deduced to `std::initializer_list`). I have read N3922 which says this deduction is considered a defect in C++14. So this means, retroactively, even programs written against older standard versions are considered ill-formed? – Arne Vogel Jan 22 '18 at 17:20
  • 4
    @ArneVogel I would suppose so. The answer quoted Clang’s explicit caveat that the change was backward-incompatible and applied to *all language versions*. – Yongwei Wu Apr 30 '18 at 00:42