<html>
<head><title>N2426 - Class member initializers</title></head>
<body>

Document Number: N2426=07-0296<br/>
Date: 2007-10-02<br/>
Reply to: Bill Seymour &lt;<a href="mailto:bill-at-the-office@pobox.com">bill-at-the-office@pobox.com</a>&gt;<br/>

<center>

<h1>Class member initializers</h1>
<h3>Michael Spertus<br/>Bill Seymour</h3>
</center>

<h2>Abstract</h2>

We propose allowing the use of initializers for non-static class and
struct data members. The purpose of this is to increase maintainability,
reduce the risk of subtle errors in complex program code, and to make the
use of initializers more consistent.

<p><hr size=5>

<h2>The proposal</h2>

The basic idea is to allow non-static data members of class and struct types to be
initialized where declared. All of the same initialization syntaxes may be used
as for initialization of local variables; and other analyses and suggestions for
improving initialization of local variables as in
<a name="initdocs">
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/N1493.pdf">N1493</a>,
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/N1509.pdf">N1509</a>,
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/N1584.pdf">N1584</a>,
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/N1701.pdf">N1701</a>,
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/N1806.html">N1806</a>,
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/N1824.htm">N1824</a>, and
<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/N1919.pdf">N1919</a>
</a>
may also be applied <i>mutatis mutandis</i> to non-static <tt>class</tt> and
<tt>struct</tt> data members.

<p>As a simple example,
<pre>
    class A {
    public:
        int a = 7;
    };
</pre>
would be equivalent to
<pre>
    class A {
    public:
        A() : a(7) {}
    };
</pre>
The real benefits of member initializers do not become apparent until a class
has multiple constructors. For many data members, especially private ones, all
constructors initialize a data member to a common value as in the next example:
<pre>
    class A {
    public:
        A(): a(7), b(5), hash_algorithm("MD5"), s("class A example") {}
        A(int a_val) : a(a_val), b(5), hash_algorithm("MD5"), s("Constructor run") {}
        A(int b_val) : a(7), b(b_val), hash_algorithm("MD5"), s("Constructor run") {}
        A(D d) : a(f(d)), b(g(d)), hash_algorithm("MD5"), s("Constructor run") {}
        int a, b;
    private:
        // Cryptographic hash to be applied to all A instances
        HashingFunction hash_algorithm;
        // String indicating state in object lifecycle
        std::string s;
    };
</pre>
Even in this simple example, the redundant code is already problematic if the
constructor arguments for <tt>hash_algorithm</tt> are copied incorrectly in one of
<tt>A</tt>&rsquo;s constructors or if one of the lifecycle states was accidentally misspelled as
<tt>"Constructor Run"</tt>. These kinds of errors can easily result in subtle bugs.
Such inconsistencies are readily avoided using member initializers.
<pre>
    class A {
    public:
        A(): a(7), b(5) {}
        A(int a_val) : a(a_val), b(5) {}
        A(int b_val) : a(7), b(b_val) {}
        A(D d) : a(f(d)), b(g(d)) {}
        int a, b;
    private:
        // Cryptographic hash to be applied to all A instances
        HashingFunction hash_algorithm("MD5");
        // String indicating state in object lifecycle
        std::string s("Constructor run");
    };
</pre>

Not only does this eliminate redundant code that must be manually synched, it
makes much clearer the distinctions between the different constructors.
(Indeed, in Java, where both forms of initialization are available,
the use of member initializers is invariably preferred by experienced
Java programmers in examples such as these.)

<p>Now suppose that it is decided that MD5 hashes are not collision resistent
enough and that SHA-1 hashes should be used. Without member initializers, all
the constructors need to be updated. Unfortunately, if one developer is unaware
of this change and creates a constructor that is defined in a different source
file and continues to initialize the cryptographic algorithm to MD5, a very hard
to detect bug will have been introduced. It seems better to keep the information
in one place.

<p>It may happen that a data member will usually have a particular value, but a
few specialized constructors will need to be cognizant of that value. If a
constructor initializes a particular member explicitly, the constructor initialization
overrides the member initializations as shown below:
<pre>
    class A {
    public:
        A(): a(7), b(5) {}
        A(int a_val) : a(a_val), b(5) {}
        A(int b_val) : a(7), b(b_val) {}
        A(D d) : a(f(d)), b(g(d)) {}
        // Copy constructor
        A(const A& aa) : a(aa.a),
                         b(aa.b),
                         hash_algorithm(aa.hash_algorithm.getName()),
                         s(aa.s) {}
        int a, b;
    private:
        // Cryptographic hash to be applied to all A instances
        HashingFunction hash_algorithm("MD5");
        // String indicating state in object lifecycle
        std::string s("Constructor run");
    };
</pre>
A few additional points are worth noting.
<ul>
<li>By allowing non-static data members of classes to be initialized in the same
    way as non-static local variables (which should be thought of as nonstatic
    data members of the function frame), C++ initialization becomes more
    consistent. As many of the <a href="#initdocs">documents mentioned above</a>
    point out, this is much needed.
<p><li>Because these initializers must be given in the class definition, which
    may be included in many files, it is possible that the initializers may vary
    in value depending on context. Although this is troublesome, the same
    problem already exists for default arguments, inline methods, member
    types, etc. Therefore member initializers do not introduce this problem
    and indeed do not appreciably aggravate it.
<p><li>There is some overlap between this proposal and constructor delegation and forwarding
    (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/N1445.htm">N1445</a>,
    <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/N1581.pdf">N1581</a>,
    <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2004/N1618.pdf">N1618</a>, and
    <a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/N1898.pdf">N1898</a>).
    However, it is easy to see that neither technique obviates the other. For example,
    overriding member initializers appears difficult to do in generality through
    constructor forwarding.  (And note that Java has both member initialization and
    constructor forwarding.  This is not regarded as confusing, and most experienced
    Java programmers make regular use of both techniques based on applicability.)
<p><li>Member initializers make possible the use of copy-initialization for class
    data members. It seems likely this flexibility will prove useful just as in the
    case of local variables.
<p><li>It is intended that this proposal also supports initializer lists
    (<a href="http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2215.pdf">N2215</a>)
    if they are adopted into C++0X.
</ul>

<p><hr size=5>

<h2>Suggested changes to the Working Paper, N2369</h2>

<h4>5.1 Primary expressions [expr.prim]</h4>

Add &ldquo;or in an <i>initializer</i> for a non-static data member (9.2)&rdquo; to 5.1p3:

<blockquote>
The keyword <tt>this</tt> names a pointer to the object for which a non-static member
function (9.3.2) is invoked. The keyword <tt>this</tt> shall be used only inside
a non-static class member function body (9.3) <ins>or in an <i>initializer</i> for
a non-static data member (9.2)</ins>.  The type of the expression is a pointer
to the function&rsquo;s class (9.3.2), possibly with cv-qualifiers
on the class type.  The expression is an rvalue.
</blockquote>

<p><hr>

Add a bullet to 5.1p11:

<blockquote>
An <i>id-expression</i> that denotes a non-static data member or
non-static member function of a class can only be used:
<blockquote>
&mdash; as part of a class member access (5.2.5) in which the
object-expression refers to the members class or a class
derived from that class, or
<p>&mdash; to form a pointer to member (5.3.1), or
<p>&mdash; in the body of a non-static member function of that class
or of a class derived from that class (9.3.1), or
<p>&mdash; in a <i>mem-initializer</i> for a constructor for that class or
for a class derived from that class (12.6.2), or
<p><ins>&mdash; in an <i>initializer</i> for a data member of that class or
of a class derived from that class (12.6.2), or</ins>
<p>&mdash; if that <i>id-expression</i> denotes a non-static data member
and it is the sole constituent of an unevaluated operand,
except for optional enclosing parentheses. [ Example:
<br>...
</blockquote>
</blockquote>

<p><hr size=3>

<h4>7.1.6.4 <tt>auto</tt> specifier [dcl.spec.auto]</h4>

Change paragraph 4:
<blockquote>
The <tt>auto</tt> <i>type-specifier</i> can also be used in declaring an object
in the <i>condition</i> of a selection statement (6.4) or an iteration statement (6.5),
in the <i>type-specifier-seq</i> in a <i>new-type-id</i> (5.3.4), and in declaring
a <del>static</del> data member with
<del>a <i>constant-initializer</i></del> <ins>an <i>initializer</i></ins>
that appears within the <i>member-specification</i> of a class definition (9.4.2).
</blockquote>

<p><hr size=3>

<h4>9.2 Class members [class.mem]</h4>

Change <i>constant-initializer</i> to <i>initializer</i> in the second line of <i>member-declarator</i>:

<blockquote>
    <i>member-declarator:</i>
    <br><tt>&nbsp;&nbsp;&nbsp;&nbsp;</tt><i>declarator&nbsp;&nbsp;pure-specifier<small><sub>opt</sub></small></i>
    <br><tt>&nbsp;&nbsp;&nbsp;&nbsp;</tt><i>declarator&nbsp;&nbsp;<del>constant-initializer</del>&nbsp;<ins>initializer</ins><small><sub>opt</sub></small></i>
    <br><tt>&nbsp;&nbsp;&nbsp;&nbsp;</tt><i>identifier<small><sub>opt</sub></small></i>&nbsp;&nbsp;<tt>:</tt>&nbsp;&nbsp;<i>constant-expression</i>
</blockquote>

<p><hr>

Delete the final rule entirely:
<blockquote>
<del><i>constant-initializer:<br><tt>&nbsp;&nbsp;&nbsp;&nbsp;</tt>= constant-expression</i></del>
</blockquote>

<p><hr>

Delete 9.2p4 entirely:
<blockquote>
<del>A <i>member-declarator</i> can contain a <i>constant-initializer</i> only if it declares
a <tt>static</tt> member (9.4) of <tt>const</tt> literal type, see 9.4.2.</del>
</blockquote>

<p><hr size=3>

<h4>9.2.4 Static data members [class.static.data]</h4>

In two places in 9.2.4p3, change &ldquo; a<i>constant-initializer</i>&rdquo;
to &ldquo;an <i>initializer</i> that is a constant expression (5.19)&rdquo;:

<blockquote>
If a <tt>static</tt> data member is of <tt>const</tt> literal type,
its declaration in the class definition can specify
<del>a <i>constant-initializer</i></del> <ins>an <i>initializer</i>
that is a constant expression (5.19)</ins>.
A <tt>static</tt> data member of literal type can be declared in the class definition
with the <tt>constexpr</tt> specifier; if so, its declaration shall specify
<del>a <i>constant-initializer</i></del> <ins>an <i>initializer</i>
that is a constant expression (5.19)</ins>.
In both these cases, the member may appear in integral constant expressions.
The member shall still be defined in a namespace scope if it is used in the program
and the namespace scope definition shall not contain an <i>initializer</i>.
</blockquote>

<p><hr size=3>

<h4>12.1 Constructors [class.ctor]</h4>

Add a new bullet between the first and second in 12.1p5:

<blockquote>
A <i>default</i> constructor for a class X is a constructor of class X that can be called
without an argument. If there is no user-declared constructor for class X,
a default constructor is implicitly declared. An implicitly-declared default constructor is
an <tt>inline</tt> <tt>public</tt> member of its class. A default constructor is <i>trivial</i>
if it is not user-provided (8.4) and if:
<blockquote>
<p>&mdash; its class has no virtual functions (10.3) and no virtual base classes (10.1), and
<p><ins>&mdash; no non-static data member of its class has an <i>initializer</i>, and</ins>
<p>&mdash; all the direct base classes of its class have trivial default constructors, and
<p>&mdash; for all the non-static data members of its class that are of class type
(or array thereof), each such class has a trivial default constructor.
</blockquote>
</blockquote>

<p><hr size=3>

<h4>12.6.2 Initializing bases and members [class.base.init]</h4>
Add a new paragraph and an example between 12.6.2p3 and 12.6.2p4:
<blockquote>
If a given non-static data memter has both an <i>initializer</i>
and a <i>mem-initializer</i>,
the initialization specified by the <i>mem-initializer</i> is performed,
and the non-static data member&rsquo;s <i>initializer</i> is ignored.
<p>[ <i>Example:</i> given
<pre>
struct A {
    int i = /* some integer expression with side effects */ ;
    A(int arg) : i(arg) { }
    // ...
};
</pre>
the <tt>A(int)</tt> constructor will simply initialize <tt>i</tt> to the value
of <tt>arg</tt>, and the side effects in <tt>i</tt>&rsquo;s initializer
will not take place. &mdash; <i>end example</i> ]
</blockquote>

<p><hr>

<p>Add to the first sentence of the current 12.6.2p4:

<blockquote>
If a given non-static data member or base class is not named by a <i>mem-initializer-id</i>
(including the case where there is no <i>mem-initializer-list</i> because the constructor
has no <i>ctor-initializer</i>), <ins>and if the given non-static data member has
no <i>initializer</i>,</ins> then
</blockquote>

<p><hr size=3>

<h4>12.7 Construction and destruction [class.cdtor]</h4>

Add &ldquo;<i>initializer</i> or&rdquo; in the second sentence of 12.7p4:

<blockquote>
Member functions, including virtual functions (10.3), can be called
during construction or destruction (12.6.2). When a
virtual function is called directly or indirectly from a constructor
(including from the <ins><i>initializer</i> or</ins> <i>mem-initializer</i>
for a data member) or from a destructor, and the object to which the call
applies is the object under construction or destruction, the function
called is the one defined in the constructor or destructor&rsquo;s own class
or in one of its bases, but not a function overriding it in a class derived
from the constructor or destructor&rsquo;s class, or overriding it in one of
the other base classes of the most derived object (1.8). If the virtual
function call uses an explicit class member access (5.2.5) and the
object-expression refers to the object under construction or destruction
but its type is neither the constructor or destructor&rsquo;s own class or
one of its bases, the result of the call is undefined. [ Example:
<br>...
</blockquote>

<p><hr>

<p>Same for 12.7p5:
<blockquote>
The <tt>typeid</tt> operator (5.2.8) can be used during construction
or destruction (12.6.2). When <tt>typeid</tt> is used in a constructor
(including from the <ins><i>initializer</i> or</ins> <i>mem-initializer</i>
for a data member) or in a destructor, or used in a function called
(directly or indirectly) from a constructor or destructor, if the operand
of <tt>typeid</tt> refers to the object under construction or destruction,
<tt>typeid</tt> yields the <tt>std::type_info</tt> object representing
the constructor or destructor&rsquo;s class. If the operand of <tt>typeid</tt>
refers to the object under construction or destruction and the static type
of the operand is neither the constructor or destructor&rsquo;s class
nor one of its bases, the result of <tt>typeid</tt> is undefined.
</blockquote>

<p><hr>

<p>And for 12.7p6:
<blockquote>
<tt>Dynamic_cast</tt>s (5.2.7) can be used during construction or destruction (12.6.2).
When a <tt>dynamic_cast</tt> is used in a constructor (including from the
<ins><i>initializer</i> or</ins> <i>mem-initializer</i> for a data member)
or in a destructor, or used in a function called (directly or indirectly)
from a constructor or destructor, if the operand of the <tt>dynamic_cast</tt>
refers to the object under construction or destruction, this object is considered
to be a most derived object that has the type of the constructor or destructor&rsquo;s
class. If the operand of the <tt>dynamic_cast</tt> refers to the object under
construction or destruction and the static type of the operand is not a pointer
to or object of the constructor or destructor&rsquo;s own class or one of its bases,
the <tt>dynamic_cast</tt> results in undefined behavior.
</blockquote>

<p><hr size=3>

<h4>12.8 Copying class objects [class.copy]</h4>

Add a note after the first sentence of 12.8p8:
<blockquote>
The implicitly-defined or explicitly-defaulted copy constructor for class X performs
a memberwise copy of its subobjects. <ins>[ <i>Note:</i> initializers
of non-static data members are ignored.
See also the example in 12.6.2p4. &mdash; <i>end note</i> ]</ins>
The order of copying is the same as the order of initialization of bases and members
in a user-defined constructor (see 12.6.2). Each subobject is copied in the manner
appropriate to its type:
</blockquote>

<p><hr size=5>
</body>
</html>
