Reputation: 938
#include <iostream>
int main()
{
auto n {42};
cout << "The n has value of " << n <<
" and a size of " << sizeof(n) << endl; // Works as expected
}
#include <iostream>
int main()
{
auto n = {42};
cout << "The n has value of " << n <<
" and a size of " << sizeof(n) << endl; // Does not work!
}
Why is that? In "A Tour of C++" it is explicitly said:
1.4.2 Initialization Before an object can be used, it must be given a value. C++ offers a variety of notations for expressing initialization, such as the = used above, and a universal form based on curly-brace delimited initializer lists:
double d1 = 2.3; // initialize d1 to 2.3
double d2 {2.3}; // initialize d2 to 2.3
double d3 = {2.3}; // initialize d3 to 2.3 (the = is optional with { ... })
complex<double> z2 {d1,d2};
complex<double> z3 = {d1,d2}; // the = is optional with { ... }
The =
is optional with {}
.
so, why does this happen?
Upvotes: 1
Views: 221
Reputation: 73166
This is governed by the rules of [dcl.type.auto.deduct], particularly [dcl.type.auto.deduct]/1 and [dcl.type.auto.deduct]/4 [emphasis mine]:
[dcl.type.auto.deduct]/1
Placeholder type deduction is the process by which a type containing a placeholder type is replaced by a deduced type.
[dcl.type.auto.deduct]/4
If the placeholder is the
auto
type-specifier, the deduced typeT'
replacingT
is determined using the rules for template argument deduction. ObtainP
fromT
by replacing the occurrences ofauto
with either a new invented type template parameterU
or, if the initialization is copy-list-initialization, withstd::initializer_list<U>
. Deduce a value forU
using the rules of template argument deduction from a function call, whereP
is a function template parameter type and the corresponding argument ise
. If the deduction fails, the declaration is ill-formed. Otherwise,T'
is obtained by substituting the deducedU
intoP
. [ 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 ]
Your second example is using copy-list-initialization, meaning auto
is replaced by, using the rules for template argument deduction, std::initializer_list<U>
, where U
is furthermore deduced as int
.
auto n {42}; // deduced type is 'int'
auto m = {42}; // deduced type is 'std::initializer_list<int>'
These rules particularly apply to placeholder type deduction, and does thus not apply to the case of non-placeholder types; the latter example of the op already specifies the type and non type deduction applies.
// no placeholder type: type is 'double', and 'd1'
// through 'd3' simply uses different ways to initialize
// an object of (non-class fundamental) type 'double'.
double d1 = 2.3; // copy initialization
double d2 {2.3}; // direct-list initialization; no narrowing allowed
double d3 = {2.3}; // copy-list-initialization (from C++11); no narrowing allowed
Upvotes: 2
Reputation: 40842
For both cases double d1 = {2.3};
and auto d2 = {2.3};
the {2.3}
is an initlaizer list.
For double d3{2.3};
and auto d4{2.3};
the {2.3}
is not an initializer list.
But for the first case, you specify for d1
what type it has, so the initializer list is used to set the value of d1
. But for d2
you tell the compiler to choose the type, so it will be the initializer list.
So =
is not optional, the difference is just not directly observable if you define the type for d1
.
Upvotes: 0
Reputation: 17678
There is subtle difference between the two initialisation:
auto n {42}; // n is of type int
auto m = {42}; // m is of type initializer_list
What Is a Curly-Brace Enclosed List If Not an intializer_list?
typeid
can be used to find the exact type of a variable, for example via typeid(x).name()
Upvotes: 1