auto&&uref3 = 27; // 27 is int and rvalue,
// so uref3's type is int&&
Item 1concludes with a discussion of how array and function names decay into pointers for non-reference type specifiers. That happens in autotype deduction, too:
const char name[] = // name's type is const char[13]
"R. N. Briggs";
autoarr1 = name; // arr1's type is const char*
auto&arr2 = name; // arr2's type is
// const char (&)[13]
void someFunc(int, double); // someFunc is a function;
// type is void(int, double)
autofunc1 = someFunc; // func1's type is
// void (*)(int, double)
auto&func2 = someFunc; // func2's type is
// void (&)(int, double)
As you can see, autotype deduction works like template type deduction. They're essentially two sides of the same coin.
Except for the one way they differ. We'll start with the observation that if you want to declare an intwith an initial value of 27, C++98 gives you two syntactic choices:
intx1 = 27;
intx2(27);
C++11, through its support for uniform initialization, adds these:
intx3 = { 27 };
intx4{ 27 };
All in all, four syntaxes, but only one result: an intwith value 27.
But as Item 5explains, there are advantages to declaring variables using auto instead of fixed types, so it'd be nice to replace intwith autoin the above variable declarations. Straightforward textual substitution yields this code:
autox1 = 27;
autox2(27);
autox3 = { 27 };
autox4{ 27 };
These declarations all compile, but they don't have the same meaning as the ones they replace. The first two statements do, indeed, declare a variable of type intwith value 27. The second two, however, declare a variable of type std::initializer_listcontaining a single element with value 27!
auto x1 = 27; // type is int , value is 27
auto x2(27); // ditto
auto x3 = { 27 }; // type is std::initializer_list ,
// value is { 27 }
auto x4{ 27 }; // ditto
This is due to a special type deduction rule for auto. When the initializer for an auto-declared variable is enclosed in braces, the deduced type is a std::initializer_list. If such a type can't be deduced (e.g., because the values in the braced initializer are of different types), the code will be rejected:
auto x5 = { 1, 2, 3.0}; // error! can't deduce T for
// std::initializer_list
As the comment indicates, type deduction will fail in this case, but it's important to recognize that there are actually two kinds of type deduction taking place. One kind stems from the use of auto: x5's type has to be deduced. Because x5's initializer is in braces, x5must be deduced to be a std::initializer_list. But std::initializer_listis a template. Instantiations are std::initializer_listfor some type T, and that means that T's type must also be deduced. Such deduction falls under the purview of the second kind of type deduction occurring here: template type deduction. In this example, that deduction fails, because the values in the braced initializer don't have a single type.
The treatment of braced initializers is the only way in which autotype deduction and template type deduction differ. When an auto-declared variable is initialized with a braced initializer, the deduced type is an instantiation of std::initializer_list. But if the corresponding template is passed the same initializer, type deduction fails, and the code is rejected:
autox = { 11, 23, 9 }; // x's type is
// std::initializer_list
template // template with parameter
void f( Tparam); // declaration equivalent to
// x's declaration
f( { 11, 23, 9 }); // error! can't deduce type for T
However, if you specify in the template that paramis a std::initializer_listfor some unknown T, template type deduction will deduce what Tis:
template
void f( std::initializer_list<T >initList);
f( { 11, 23, 9 }); // T deduced as int, and initList's
// type is std::initializer_list
So the only real difference between autoand template type deduction is that auto assumes that a braced initializer represents a std::initializer_list, but template type deduction doesn't.
You might wonder why autotype deduction has a special rule for braced initializers, but template type deduction does not. I wonder this myself. Alas, I have not been able to find a convincing explanation. But the rule is the rule, and this means you must remember that if you declare a variable using autoand you initialize it with a braced initializer, the deduced type will always be std::initializer_list. It's especially important to bear this in mind if you embrace the philosophy of uniform initialization — of enclosing initializing values in braces as a matter of course. A classic mistake in C++11 programming is accidentally declaring a std::initializer_listvariable when you mean to declare something else. This pitfall is one of the reasons some developers put braces around their initializers only when they have to. (When you have to is discussed in Item 7.)
For C++11, this is the full story, but for C++14, the tale continues. C++14 permits autoto indicate that a function's return type should be deduced (see Item 3), and C++14 lambdas may use autoin parameter declarations. However, these uses of autoemploy template type deduction, not autotype deduction. So a function with an autoreturn type that returns a braced initializer won't compile:
autocreateInitList() {
return { 1, 2, 3 }; // error: can't deduce type
} // for { 1, 2, 3 }
The same is true when autois used in a parameter type specification in a C++14 lambda:
std::vector v;
…
auto resetV =
[&v](const auto& newValue) { v = newValue; }; // C++14
…
resetV( { 1, 2, 3 }); // error! can't deduce type
// for { 1, 2, 3 }
Things to Remember
• autotype deduction is usually the same as template type deduction, but auto type deduction assumes that a braced initializer represents a std::initializer_list, and template type deduction doesn't.
• autoin a function return type or a lambda parameter implies template type deduction, not auto type deduction.
Читать дальше