In his CppCon 2014 talke "Type Deduction and Why You Care", Scott Meyers raises the question why there is a special rule about auto
and braced initializers in the C++11/C++14 standard (his question starts at 36m05s).
The semantic of auto in combination with a braced-init-list is defined in §7.1.6.4/6.
I thought about it and could not come up with a use-case either. The closest thing that I have seen so far is one example where Bjarne Stroustrup used it.
In his Cpp 2014 talk "Make Simple Tasks Simple!", he once uses auto
to capture initializers (but only as a workaround).
Here is the code (part of slide 30, at 37m10s):
// auto ss1 = collect({ 1, 2, 3, 4, 5, 6 }, odd); // error: Bummer!
auto lst = { 1, 2, 3, 4, 5, 6 };
auto ss2 = collect(lst, odd); // {1,3,5}
But note that it is only a workaround. He mentioned that it should not be necessary. Instead he would prefer to directly pass the arguments to the function. So, it cannot really serve as a good motivation for auto
and initializer lists.
My understanding of C++ is not deep enough to judge the downsides of allowing initializer-lists in Bjarne's example, as he proposes. Anyway, it would avoid the need for auto
in that case.
So, is auto
and initializer list only a workaround for something that could have been better solved? Or are there good examples, where the extra auto deduction rule in §7.1.6.4/6 is useful?
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 type T
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 function f(T)
.
Scott Meyers addressed the topic in a blog post: Why auto deduces std::initializer_list for a braced initializer
Like T.C.'s answer, it also refers to N2640. The special deduction rule has been added to allow code like this to work:
auto x = { 1, 1, 2, 3, 5 };
f(x);
g(x);
In the blog post, Scott cites the following explanation from James Hopkin:
The short story is that N2640 proposed the special case that auto should deduce braced initializers as initializer_lists, without realizing that doing so broke uniform initialization (e.g. it makes int x{7};
and auto x{7};
very different). N3922 fixes that by (of course!) introducing another special case: single parameter braced initializers have their own rule.
Slightly more detail: N2640 tries to keep template argument deduction simple but attempts to allow a braced initializer to be passed to two or more functions via assigning it to an auto. This got turned into wording in N2672. Note that Stroustrup's previous design in N2532 allows deduction of initializer_lists for both unconstrained template parameters and auto, which is more consistent but also breaks uniform initialization.
None of this explains why N3922 didn't just remove the special case for auto. That wouldn't have resulted in silent changes to the meaning of code and would have simplified the language.