1
#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?

tornikeo
  • 915
  • 5
  • 20
  • 1
    It's working just fine, but `{42}` by itself is a `std::initializer_list`, not an `int`. The foolproof way to create a variable that you don't get as the result of something else is `auto name = type{ args };` — and actually even that will run into issues in c++11 if the type is not move constructible (this is fixed in c++17). – spectras Aug 29 '20 at 09:24
  • Fwiw, [this answer](https://stackoverflow.com/a/37682992/3212865) shows the subtleties of initializer lists, initializer_lists and braced initializer lists. – spectras Aug 29 '20 at 09:34
  • "Does not work!"? Please state how it does not work. If there is an error, quote it (don't post a screenshot) in entirety. – Yakk - Adam Nevraumont Aug 29 '20 at 13:54

3 Answers3

2

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 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. Otherwise, T' is obtained by substituting the deduced U into P. [ 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
dfrib
  • 70,367
  • 12
  • 127
  • 192
1

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()

artm
  • 17,291
  • 6
  • 38
  • 54
0

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.

t.niese
  • 39,256
  • 9
  • 74
  • 101