﻿<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01//EN"
   "http://www.w3.org/TR/html4/strict.dtd">
<html>
<head>

<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">

<style type="text/css">

body { color: #000000; background-color: #FFFFFF; }
del { text-decoration: line-through; color: #8B0040; }
ins { text-decoration: underline; color: #005100; }

p.example { margin-left: 2em; }
pre.example { margin-left: 2em; }
div.example { margin-left: 2em; }

code.extract { background-color: #F5F6A2; }
pre.extract { margin-left: 2em; background-color: #F5F6A2;
  border: 1px solid #E1E28E; }

p.function { }
.attribute { margin-left: 2em; }
.attribute dt { float: left; font-style: italic;
  padding-right: 1ex; }
.attribute dd { margin-left: 0em; }

blockquote.std { color: #000000; background-color: #F1F1F1;
  border: 1px solid #D1D1D1;
  padding-left: 0.5em; padding-right: 0.5em; }
blockquote.stddel { text-decoration: line-through;
  color: #000000; background-color: #FFEBFF;
  border: 1px solid #ECD7EC;
  padding-left: 0.5empadding-right: 0.5em; ; }

blockquote.stdins { text-decoration: underline;
  color: #000000; background-color: #C8FFC8;
  border: 1px solid #B3EBB3; padding: 0.5em; }

table { border: 1px solid black; border-spacing: 0px;
  margin-left: auto; margin-right: auto; }
th { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }
td { text-align: left; vertical-align: top;
  padding-left: 0.8em; border: none; }

</style>

<title>Member initializers and aggregates</title>
</head>
<body>
<h1>Member initializers and aggregates</h1>

<p>
ISO/IEC JTC1 SC22 WG21 N3605 - 2013-03-15
</p>

<address>
Ville Voutilainen, ville.voutilainen@gmail.com
</address>

<h2><a name="Abstract">Abstract</a></h2>

Bjarne Stroustrup and Richard Smith raised an issue about
aggregate initialization and member-initializers not working
together. This paper proposes to fix the issue by adopting
Smith's proposed wording that removes a restriction that aggregates
can't have member-initializers.

<h2><a name="Introduction">Introduction</a></h2>

<p>
In c++std-core-23029, Bjarne Stroustrup pointed out that aggregate
initialization can't be used if a class has member-initializers.
He provided the following example:

<pre>
<code>
struct Univ {
    string name;
    int rank;
    string city = "unknown";
};

void t1()
{
    Univ u = {"Columbia",10};
    cout << u.name << ' ' << u.rank << ' ' << u.city << '\n';
}
</code>
</pre>

Ville Voutilainen, Vinny Romano and Jonathan Wakely explained that this is 
caused by the restrictions in [decl.init.aggr]/1, which says

<blockquote>
An aggregate is an array or a class (Clause 9) with no user-provided constructors (12.1), no brace-or-equal-
initializers for non-static data members (9.2), no private or protected non-static data members (Clause 11),
no base classes (Clause 10), and no virtual functions (10.3).
</blockquote>

The issue here is the "no brace-or-equal-initializers for non-static data
members". Also, [dcl.init.list]/3 specifies that

<blockquote>
List-initialization of an object or reference of type T is defined as follows:
<p>- If T is an aggregate, aggregate initialization is performed (8.5.1).</p>
</code>
</blockquote>

Thus, aggregate initialization can't be used because the class type
in question is not an aggregate.
</p>

<h2><a name="Proposed_solution">Proposed solution</a></h2>

<p>
Johannes Schaub pointed out that this issue had been raised by Richard
Smith earlier, and a proposed solution had been presented in 
[c++std-ext-13002]. Smith proposes to just remove the restriction
that prevents aggregates from having member initializers, thus:

<p>
Change in 8.5.1 (dcl.init.aggr) paragraph 1:
<blockquote>
An aggregate is an array or a class (Clause 9) with no user-provided
constructors (12.1), <del>no brace-or-equal-
initializers for non-static data members (9.2),</del> no private or
protected non-static data members (Clause 11),
no base classes (Clause 10), and no virtual functions (10.3).
</blockquote>

Change in 8.5.1 paragraph 7:
<blockquote>
If there are fewer initializer-clauses in the list than there are members
in the aggregate, then each member not explicitly initialized shall be
initialized from <ins>its brace-or-equal-initializer, if present, or
otherwise from</ins> an empty initializer list (8.5.4). [ Example:
  struct S { int a; const char* b; int c; <ins>int d = b[a];</ins> };
  S ss = { 1, "asdf" };
initializes ss.a with 1, ss.b with "asdf", <del>and</del> ss.c with the
value of an expression of the form int<ins>{}</ins><del>()</del>, that is,
0<ins>, and ss.d with the value of ss.b[ss.a], that is, 's'</ins>. -end
example ]
</blockquote>
</p>
</p>

<h2><a name="Concerns_about_the_solution">Concerns about the solution</a></h2>

<p>
Mike Miller voiced some concern whether we should just allow aggregates to
have member-initializers, or whether something more generic would be more
appropriate:
<blockquote>
That said, however, if we adopt Richard's approach, it seems like a
straightforward enough change that I'd be willing for CWG to take it.
My only question is whether that really is what we want to do.
That limits the utility of omitting initializers to trailing members,
much like default arguments.  Maybe EWG should take a quick
pass over it to see if this is sufficient or if we want something more
comprehensive, like designated initializers or initializer adapters or
some such.  (Bjarne rightly warns about a multitude of small,
unconnected extensions, which this strikes me as being.)
</blockquote>

Miller further clarified that with such a solution, it's possible
to provide fewer initializers than there are members, but the omitted
ones must be for the members at the end:
<blockquote>
It's just like default arguments: you have to declare your non-static
data members in order of how likely they are to use the default
value.  If you have a dozen non-static data members and, for
some reason, you only need to specify a non-default value for
the 12th one, you still have to give explicit initializers for the
other eleven because the association is only by lexical ordering.
Designated initializers (or something more C++-like, some sort
of adapter, perhaps) would allow you to provide only the
non-default initializers.
</blockquote>

Daniel Krügler pointed out teachability issues for aggregates:
<blockquote>
Aggregates cannot have user-defined constructors
and member-initializers are essentially some kind of user-defined
constructor (element) (see also <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects.html#886">Core Defect 886</a>). I'm
not against this extension, but it also has implications on what our
model of aggregates actually is. After acceptance of this extension I
would like to know how to teach what an aggregate is.
</blockquote>
Krügler also added the following:
<blockquote>
The term "aggregate" is very popular beyond the standard text. People
often ask what an aggregate is, because it is (or at least was) a
fundamental type characteristic.
</blockquote>

Clark Nelson asked whether the proposed fix is as simple as people think:
<blockquote>
Personally, I think this is going to be far more than just a bug fix, and I think it's pretty unlikely to make 2014. For example, what does this mean:
<pre>
<code>
struct X { int i, j, k = 42; };

X a[] = { 1, 2, 3, 4, 5, 6 };
</code>
</pre>
Does "a" have two elements or three? Is it actually possible to override the "default" value for k? Either answer is going to disappoint someone.
</blockquote>

Stroustrup asked whether the initialization of a above should just be ambiguous.

Mike Miller suggested the following:
<blockquote>
My perhaps naive expectation was that brace elision would continue
to work as it always has and that a non-static data member
initializer in an aggregate would be as analogous as possible to one
in a class with a constructor: i.e., if there's an explicit initializer for
the member, it prevails, and the non-static data member initializer
would be used only when the member would otherwise have been
default-initialized.  I.e., that example is well-formed, giving a two
elements, with a[0].k being 3 and a[1].k being 6.  That would seem
to me to be the least surprising outcome, although it might be not
the preferred outcome.  Other outcomes could be specified, as you
illustrated, using explicit bracing.
</blockquote>

Nikolay Ivchenkov provided the following example:
<blockquote>
With regard to initialization of members:

<pre>
<code>
    #include <iostream>

    struct A
    {
        struct X { int a, b; };
        X x = { 1, 2 };
        int n;
    };

    int main()
    {
        A a = {{10}, 5};
        std::cout << a.x.b << std::endl;
    }
</pre>
</code>
How should a.x.b be initialized here?
</blockquote>
Miller explained:
<blockquote>
My simple mental model of this says that you only use a
non-static data member initializer if the member is not explicitly
initialized.  In this case, a.x does have an initializer -- "{10}" --
so the non-static data member initializer is ignored, resulting in
a.x.b having the value 0.
</blockquote>
</p>

<h2><a name="The_solution_proposed_by_this_paper">The solution proposed by this paper</a></h2>

<p>
The proposed solution is as proposed by Richard Smith; simply fix the problem
by allowing aggregates to have member-initializers, by using the wording
Smith provided. Regarding Daniel Krügler's
concerns about the concept of an aggregate and its teachability, the author
of this paper thinks the benefit of allowing member-initializers in aggregates
outweighs such concerns. It would perhaps be possible to keep the definition
of an aggregate as is, add a new concept of an extended aggregate that would allow member initializers, and
modify the rest of [dcl.init.aggr] to apply to extended aggregates as well
as regular aggregates, and modify [dcl.init.list]/3 to apply to aggregates
and extended aggregates. Due to an unfortunately busy paper author and
a looming pre-mailing deadline, such wording hasn't been created.
</p>

</body>
</html>
