Proposal to provide simple default initilization for class construtors Doc No: X3J16/95-0079 WG21/N0679 Date: 03/23/95 Author: John Bruns (johnb@crt.com) Proposal: It is now simple for a user to get initialized memory for PODS and other aggregates initialized. We also have the facility for a class designer to initialize bases and members of these types. Unfortunately, to enforce this behavior requires repetitive and error prone editing. This is especially true of simple classes with large numbers of simple data types. These types of classes are commonplace in business programming. I propose a simple extension to the semantics of the ctor initializer list to allow a class designer to declare that all such members and bases should be initialized. This extension add no new syntax. It does not change any existing program. It is consistent with the meaning we have given to T() vs T throughout the language. The implementation is relatively trivial. I believe it will provide major relief from a source of errors that are traditionally hard to diagnose and locate. ------------ Working Paper Changes: Change to to 12.6.2 para 7 The identifier ... shall denote a nonstatic data member or the type of a direct or virtual base class. It may also denote the class itself in which case the expression list must be empty. ... ... When a name denoting the class itself appears in an initializer list, the compiler interprets it as if all non-static data members and direct or virtual base classes which were not explicitly named in the initializer list, appeared in the list with empty initializer lists. struct A { double d; }; struct D: public A { double x; D(): }; D::D() : D() {}; // As if D::D() : A(), x() {}; ----------- Background: In Austin Josee's core group spent a considerable amount of time discussing what the meaning of T() was for any given type. In the end, we decided that for PODS and arrays of PODS T() meant that the memory was initialized as if the object were a static variable (ie. initialized to appropriate 0 first). If the object had a non-trivial constructor, the constructor must be written to accomplish the initialization. Page 2 PODS, et all : T t; // uninitialized T t = T(); // initialized Other X x; // call default constructor X x = X(); // call default constructor ( hopefully optimize away copy) After thinking a long time on this issue, I believe the premise is absolutely correct. If the user wants an initialized PODS, he has a simple method of getting it. Since he has total control over such structures, this external differentiation between T and T() makes sense. For true object classes, the initialization is the responsibility of the class designer, not the user. If two different constructors are required, the designer can always include an additional constructor. We simply needed to draw a line, and we did at "non-trivial constructor". We also added a method for composing classes composed of such structures. struct T { int x; double y; }; struct X : public T{ int a [10]: double d; X() ; }; X::X() : T(), a(), d() {}; // default initiaize all data; The decision, the responsibility and the capability are all in the hands of the class designer. There is a problem however. We have created a situation where it takes considerably more effort to make the decision to initialize, than the decision not to. Once an object steps out of the PODS mold, initializing it requires care in making sure every element appears on the ctor initializer list. While this is not a problem with trivial classes, consider classes consisting of many data elements, such as business objects. We are now creating a maintenance headache. To add or modify a field in a class, each constructor must be changed by hand. Failure can cause unpredictable results. It the class has 50 or so fields (not uncommon in business objects) , the ctor init line is UGLY. The sheer bulk of repetitive coding hides any significant initialization. I recommend that we fix this usability problem by adding a minor semantic change to the ctor initializer list. In particular, I propose defining T() in a ctor initializer list for the class T. This is clearly not allowed currently, because T is not a base class or member of T. Yet the syntax is legal, only the semantics are illegal. So this change provides not new syntax. Page 3 What I propose is that we allow the above constructor to read: X::X() : X() {}; // default initiaize all data; T() in an ctor initializer list would mean be equivalent to writing B() for each base class and m() for each member for each B and m not explicitly mentioned in the initializer list. This would allow bases and members that require explicit initializer to be named explicitly in the list. This change add no new functionality to the language. It is merely a shorthand. One could achieve the same result by laboriously writing out each base and member. I think it is important to point out that the equivalent already exists for the uninitialized case. X::X {}; is equivalent to no initialization for any PODS base classes, PODS members but calling B() or m() for any member without a default initializer. This is already part of the language. Adding this extension would allow the same simplicity of expression for the initialization case. I believe it fits in extremely well with our definition of T() elsewhere in the language. It would still be under the control of the class designer, but easy to express and simple to maintain. The core group trying to accomplish this with our first formulation as presented in Austin. We got off the track and provided a simple solution syntactically but had some shortcomings. First, the initialization of the object was always controlled by the user. This is a conceptual flaw, as it is the class designer who needs to maintain this control. Second, the implementation effects caused cascading, each class required both an initialized and uninitialized default constructor. This was the flaw pointed out by implementers that required us to modify our original decision. I think this proposal restores our original intent and avoids both these flaws.