Saturday, February 21, 2015

Why auto deduces std::initializer_list for a braced initializer

For some years, I've wondered aloud why
auto x { 1, 2, 3 };
deduces x's type as std::initializer_list, even though the braced initializer itself ("{1, 2, 3}") has no type. One example of my out-loud wondering took place on this blog about a year ago ("If braced initializers have no type, why is the committee so insistent on deducing one for them?"). Another was during my talk at CppCon 2014, "Type deduction and why you care". James Hopkin recently watched the video of that presentation, and he then sent me the following quite interesting message:
You ask at one point for any info on the motivation for N3922's special case for auto. I did a bit of digging and thought I'd send you what I found.

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.
For purposes of understanding why there's a special rule for auto type deduction and braced initializers, the key sentence in N2640 is this:
On the other hand, being able to deduce an initializer_list 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.
That's not a lot of detail, but it's more than I've ever seen before. Thank you, James Hopkin, for digging up this background information!

Scott

Monday, February 16, 2015

New EMC++ Sample Content is Now Online

O'Reilly has updated the Effective Modern C++ Sample Page to include a new book excerpt. This time it's

Item 7: Distinguish between () and {} when creating objects.

This is the published version of an Item I originally posted in draft form in a blog post about 11 months ago.

Enjoy!

Scott

Monday, February 2, 2015

Expressions can have Reference Type


Today I got email about some information in Effective Modern C++. The email included the statement, "An expression never has reference type." This is easily shown to be incorrect, but people assert it to me often enough that I'm writing this blog entry so that I can refer people to it in the future.

Section 5/5 of the Standard is quite clear (I've put the relevant text in bold):
If an expression initially has the type “reference to T” (8.3.2, 8.5.3), the type is adjusted to T prior to any further analysis. The expression designates the object or function denoted by the reference, and  the expression is an lvalue or an xvalue, depending on the expression.
There'd clearly be no need for this part of the Standard if expressions couldn't have reference type.

If that's not enough to settle the matter, consider the type of an expression that consists of a function call. For example:
        int& f();                // f returns int&
        auto x = f();            // a call to f
        
What is the type of the expression "f()", i.e., the type of the expression consisting of a call to f? It's hard to imagine anybody arguing that it's not int&, i.e., a reference type. But what does the Standard say? Per 5.2.2/3 (where I've again put the relevant text in bold and where I'm grateful to Marcel Wid for correcting the error I had in an earlier version of this post that referred to 5.2.2/10):
If the postfix-expression designates a destructor (12.4), the type of the function call expression is void; otherwise, the type of the function call expression is the return type of the statically chosen function (i.e., ignoring the virtual keyword), even if the type of the function actually called is different. This return type shall be an object type, a reference type or cv void.
It's very clear that expressions can have reference type. Section 5/5 takes those expressions and strips the reference-ness off of them before doing anything else, but that's not the same as the reference-ness never being present in the first place.

Scott