Why does auto x{3} deduce an initializer_list?
To make long story short:
- a braced initializer expression
{}
has no type by itself auto
has to infer type informationint{3}
obviously means "create anint
var with value taken from initializer list", thus its type is justint
and can be used in any wider context (int i = int{3}
will work andauto i = int{3}
can deduce type, because right side is obviously of typeint
){3}
by itself has no type (it can't beint
, because it's not a value but an initializer list), soauto
wouldn't work — but, because committee considered thatauto
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).
Why auto is deduced differently?
#include <initializer_list>
#include <type_traits>
using namespace std;
int main(){
int x{};
auto x2 = x;
auto x3{x};
static_assert(is_same<int, decltype(x)>::value, "decltype(x) is the same as int");
static_assert(is_same<int, decltype(x2)>::value, "decltype(x2) is the same as int");
static_assert(is_same<std::initializer_list<int>, decltype(x3)>::value, "decltype(x3) is the same as int");
}
This will compile. x3
is deduced to be a std::initializer_list<int>
due to:
Let
T
be the type that has been determined for a variable identifierd
. ObtainP
from T [...] if the initializer is a braced-init-list (8.5.4), withstd::initializer_list<U>
.
auto with parentheses and initialiser list
This is ill-formed. In short, braced-init-list can't be deduced in template argument deduction, it's considered as non-deduced context.
6) The parameter P, whose A is a braced-init-list, but P is not std::initializer_list or a reference to one:
Firstly, auto type deduction uses the rules of template argument deduction from a function call. [dcl.type.auto.deduct]/4
(emphasis mine)
If the placeholder is the auto type-specifier, the deduced type T'
replacing T is determined using the rules for template argument
deduction. Obtain P from T by replacing the occurrences of auto with
either a new invented type template parameter U or, if the
initialization is copy-list-initialization, with
std::initializer_list<U>
. Deduce a value for U using the rules of
template argument deduction from a function call, where P is a
function template parameter type and the corresponding argument is e.
If the deduction fails, the declaration is ill-formed. [ Example:const auto &i = expr;
The type of i is the deduced type of the parameter u in the call f(expr) of the following invented function template:
template <class U> void f(const U& u);
— end example ]
Note that auto x({1, 2, 3, 4});
is direct initialization, not copy initialization, then the invented type template parameter is just U
, not std::initializer_list<U>
, and the corresponding argument is {1, 2, 3, 4}
.
And in template argument deduction from a function call, template parameter can't be deduced from braced-init-list. [temp.deduct.call]/1
Template argument deduction is done by comparing each function template parameter type (call it P) that contains template-parameters that participate in template argument deduction with the type of the corresponding argument of the call (call it A) as described below. If removing references and cv-qualifiers from P gives std::initializer_list or P'[N] for some P' and N and the argument is a non-empty initializer list ([dcl.init.list]), then deduction is performed instead for each element of the initializer list, taking P' as a function template parameter type and the initializer element as its argument, and in the P'[N] case, if N is a non-type template parameter, N is deduced from the length of the initializer list. Otherwise, an initializer list argument causes the parameter to be considered a non-deduced context ([temp.deduct.type]). [ Example:
template<class T> void g(T);
g({1,2,3}); // error: no argument deduced for T
— end example ]
why this variable isn't deduced as initializer_list in g++ in C++14?
There is a proposal for C++1z that implements new type deduction rules for brace initialization
Not exactly. If you follow the link to the actual paper, it reads:
Direction from EWG is that we consider this a defect in C++14.
Which is enough to get implementors to also treat it as a defect, and hence change the compiler behaviour even in C++14 mode.
Why does g++5 deduces object instead of initializer_list in auto type deduction
There is a proposal for C++1z that implements new type deduction rules for brace initialization (N3922), and I guess gcc implemented them:
For direct list-initialization:
1. For a braced-init-list with only a single element, auto deduction will deduce from that entry;
2. For a braced-init-list with more than one element, auto deduction will be ill-formed.[Example:
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.
-- end example]
Here is the gcc patch concerning the new changes with regards to "Unicorn initialization."
Why auto cannot accept braced-init-list when deducing return value?
std::initializer_list
is a reference to an anonymous array.
The array itself lives at block scope where the initializer_list
was created.
std::initializer_list<int> f() {
return {1,2,3};
}
is almost completely useless, because the array's lifetime is the body of f
, and the initializer_list
that refers to it exists in a nearly completely disjoint period of code.
Using that initializer_list
is going to almost certainly be undefined behavior. You could probably ask its size and if it is empty, and that might be defined behavior (don't know, don't care enough to check), but you definitely cannot examine its contents.
Initializer lists are references to the data, not copies of the data.
If:
auto f() {
return {1,2,3};
}
deduced itself to be the above, it would be almost never useful.
The exception, that auto x = {1,2,3};
works, is the one and only case where initailizer_list<int>
can be deduced from a set of {}
. (In C++11, the same was true of auto x{1};
, but that was depricated).
initializer_list with auto contains multiple expressions
The rule of auto type deduction changed since N3922. (This is considered as a defect in C++14).
In direct-list-initialization (but not in copy-list-initalization),
when deducing the meaning of the auto from a braced-init-list, the
braced-init-list must contain only one element, and the type of auto
will be the type of that element:auto x1 = {3}; // x1 is std::initializer_list<int>
auto x2{1, 2}; // error: not a single element
auto x3{3}; // x3 is int
// (before N3922 x2 and x3 were both std::initializer_list<int>)
So before N3922, all the variables in your sample work fine and have type std::initializer_list<int>
. But since N3922, for direct initialization (i.e. for x11
and x22
) the braced-initializer must contain only one element (and their type would be the type of the element), then the code become ill-formed.
See N3922 and N3681 for more.
Why is there a special type deduction rule for auto and braced initializers in C++11/C++14?
The rationale is in N2640, which wanted to ban deduction of a plain type parameter from a braced initializer list in general:
template<class T>
void inc(T, int); // (1)
template<class T>
void inc(std::initializer_list<T>, long); // (2)
inc({1, 2, 3}, 3); // Calls (2). (If deduction had succeeded
// for (1), (1) would have been called — a
// surprise.)
But carved out a special exception for auto
:
On the other hand, being able to deduce an
initializer_list<X>
for
T
is attractive to allow:auto x = { 1, 1, 2, 3, 5 };
f(x);
g(x);
which was deemed desirable behavior since the very beginning of the
EWG discussions about initializer lists. Rather than coming up with a
clever deduction rule for a parameter typeT
matched with a {}-list
(an option we pursued in earlier sketches and drafts of this paper),
we now prefer to handle this with a special case for "auto" variable
deduction when the initializer is a {}-list. I.e., for the specific
case of a variable declared with an "auto" type specifier and a
{}-list initializer, the "auto" is deduced as for a function
f(initializer_list<T>)
instead of as for a functionf(T)
.
Related Topics
How to Solve "Unresolved Inclusion: <Iostream>" in a C++ File in Eclipse Cdt
Double or Float, Which Is Faster
C++11: Correct Std::Array Initialization
How to Use Sdl2 and Sdl_Image with Cmake
Why Can't You Use Offsetof on Non-Pod Structures in C++
C++ Custom Compare Function for Std::Sort()
Why Does Auto X{3} Deduce an Initializer_List
How to Pass Auto as an Argument in C++
What Is the Default Constructor for C++ Pointer
Array Initialization Use Const Variable in C++
Why Shared_From_This Can't Be Used in Constructor from Technical Standpoint
How to Update an Existing Element of Std::Set
Are There Any Way to Link My Program with Wine-Compiled Part