5

There has already been a similar question on SO, but I want to stress another aspect of braced-init-lists. Consider the following:

auto x = {1}; //(1)

This is ill-formed (8.5.4/2) unless the header <initializer_list> is included. But why? The standard says, that the template std::initializer_list is not predefined. Does this mean, that declaration (1) introduces a new type? In all other situations, where auto may be used such as

auto y = expr;

where expr is an expression, the type auto deduces already exists. On the other hand, from a logical point of view, the compiler must assign an implicite type to the construct {1}, for which std::initializer_list is then another name. But in declaration (1) we do not want to name this type. So why must this header be included. There is a similar situation with nullptr. Its type implicitely exists, but to name it explicitely you have to include <cstddef>.

Community
  • 1
  • 1
MWid
  • 4,429
  • 3
  • 20
  • 20
  • 3
    `auto x = {expr};` will deduce `x` to be of type `std::initializer_list`, per language rules - so, `#include ` is needed. – Xeo Jun 14 '13 at 10:04

1 Answers1

8

That's not the same. The rules for std::nullptr_t and std::initializer_list are actually different.

std::nullptr_t is just a typedef for a built-in type. Its definition is

namespace std {
  using nullptr_t = decltype(nullptr);
}

The type exists whether you include the header or not.

std::initializer_list is a class template, not a predefined type. It really doesn't exist unless you include the header that defines it. In particular, the initializer list { 1 } does not have type std::initializer_list<int>; it has no type at all, because it is not an expression. (Initializer lists are special syntactic constructs and cannot appear everywhere an expression can.)

std::initializer_list is just slightly special. For one, there are special rules for how to initialize a std::initializer_list from the initializer list syntax (allocate an array and have the object refer to it). However, this requires std::initializer_list to be defined in the first place.

The second special case is auto type deduction. There's a special rule here too. But again, this doesn't mean that the compiler will automatically define the type; it just means that it will recognize it.

Sebastian Redl
  • 69,373
  • 8
  • 123
  • 157
  • If we want to formalize _auto type deduction_ (type inference) then the construct `{1}` must somehow have a type. – MWid Jun 14 '13 at 10:18
  • @MWid As Sebastian stated, there's a special rule for type deduction for the `auto` specifier, see [dcl.spec.auto]/6. A *braced-init-list* is part of a declarator, not an expression itself. – dyp Jun 14 '13 at 10:37
  • @DyP I know that and that's where the trouble starts. For example, you can use a _braced-init-list_ as right operand of an assignment. Now the language rules require this operand to be evaluated. But evaluation is only defined for expressions. The point I want to make is, that we run into problems, if the construct `{1}` doesn't have in some way a type or even better, would be an expression. – MWid Jun 14 '13 at 10:43
  • @MWid: What do mean by "problems"? If you mean that the wording of the standard needs some special cases to deal with a braced-init-list not being an expression, then that's true; but the "problem" has been solved by doing just that. – Mike Seymour Jun 14 '13 at 11:35
  • @MikeSeymour No, trying to solve the problem results in new problem: In C++11 it was not allowed to pass a _braced-init-list_ as function argument, because by 5.2.2 a function argument had to be an expression. In the current C++14 draft a function argument can be an _initializer-clause_, but that only allows _assignment-expressions_ and `f(a, (t=3, t+2), c);` (example in 5.18/2) is not possible any more. – MWid Jun 14 '13 at 12:11
  • 1
    @MWid I don't know what standard you are reading. C++11 allows braced-init-list as a function argument. The contents of the call parentheses are *expression-list*, which is an alias of *initializer-list*, which is a list of *initializer-clause*, which can be an *assignment-expression* or a *braced-init-list*. And a parenthesized expression is a *primary-expression*, which is one possible form of *assignment-expression*, so the 5.18/2 example is perfectly valid too. – Sebastian Redl Jun 14 '13 at 12:16
  • @SebastianRedl You're right. I just remembered this [issue](https://github.com/cplusplus/draft/issues/35) and didn't look up the grammar rules in section 5.2. And my example about C++14 is as you remarked wrong. Nevertheless, the problem about evaluation of the operands in an assignment expression still remain. – MWid Jun 14 '13 at 12:50
  • @MWid Actually, [expr.ass]/1 requires *value computation*, not *evaluation* of both sides of an assignment. The former is only a part of the latter, see [intro.execution]/12 – dyp Jun 14 '13 at 12:56
  • @MWid Also, [dcl.init.list]/4 specifies IMO how to "evaluate" a *braced-init-list*. – dyp Jun 14 '13 at 13:01
  • @DyP No! [expr.ass]/1 requires **evaluation**. What do you a conforming implementation expect to output for `int f(){cout << "hello"; return 0;} int x = f();`? – MWid Jun 14 '13 at 13:12
  • 1
    @MWid [expr.ass] clearly defines evaluation with a case distinction, and forwarding the complex case to function call semantics. (See DR1527 though.) Function call semantics just declare that the parameters shall be initialized with the appropriate arguments, which for initializer lists is an implicit reference to [dcl.init.list]. In particular, p4 specifies that the individual initializers are evaluated in order. – Sebastian Redl Jun 14 '13 at 13:53
  • @SebastianRedl Reading the [final proposal](http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2640.pdf) page 19, a _braced-init-list_ had a type if all elements of the list had the same type `E`. I'm wondering why this wording didn't find their way into the standard. – MWid Jun 14 '13 at 14:18
  • @MWid I don't know the actual reasoning, but probably because it wouldn't be useful and cause other problems. – Sebastian Redl Jun 14 '13 at 14:19
  • I should clarify that. Thinking back on the implementation of initializer lists in Clang I can't think of a place where such a rule in the standard would have made things easier, and quite a few where it would have made things harder. – Sebastian Redl Jun 14 '13 at 14:20