Title
Value-initialization and generation of default constructor
Status
cd1
Section
9.4 [dcl.init]
Submitter
Steve Adamczyk

Created on 2001-07-23.00:00:00 last changed 196 months ago

Messages

Date: 2002-10-15.00:00:00

[Moved to DR at October 2002 meeting.]

Date: 2001-10-15.00:00:00

Proposed resolution (10/01):

Add the indicated wording to the third-to-last sentence of 6.3 [basic.def.odr] pararaph 2:

A default constructor for a class is used by default initialization or value initialization as specified in 9.4 [dcl.init].

Add a footnote to the indicated bullet in 9.4 [dcl.init] paragraph 5:

  • if T is a non-union class type without a user-declared constructor, then every non-static data member and base-class component of T is value-initialized. [Footnote: Value-initialization for such a class object may be implemented by zero-initializing the object and then calling the default constructor.]

Add the indicated wording to the first sentence of 11.4.5 [class.ctor] paragraph 7:

An implicitly-declared default constructor for a class is implicitly defined when it is used (6.3 [basic.def.odr]) to create an object of its class type (6.7.2 [intro.object]).
Date: 2004-09-10.00:00:00

We've been looking at implementing value-initialization. At one point, some years back, I remember Bjarne saying that something like X() in an expression should produce an X object with the same value one would get if one created a static X object, i.e., the uninitialized members would be zero-initialized because the whole object is initialized at program startup, before the constructor is called.

The formulation for default-initialization that made it into TC1 (in 9.4 [dcl.init]) is written a little differently (see issue 178), but I had always assumed that it would still be a valid implementation to zero the whole object and then call the default constructor for the troublesome "non-POD but no user-written constructor" cases.

That almost works correctly, but I found a problem case:

    struct A {
      A();
      ~A();
    };
    struct B {
      // B is a non-POD with no user-written constructor.
      // It has a nontrivial generated constructor.
      const int i;
      A a;
    };
    int main () {
      // Value-initializing a "B" doesn't call the default constructor for
      // "B"; it value-initializes the members of B.  Therefore it shouldn't
      // cause an error on generation of the default constructor for the
      // following:
      new B();
    }

If the definition of the B::B() constructor is generated, an error is issued because the const member "i" is not initialized. But the definition of value-initialization doesn't require calling the constructor, and therefore it doesn't require generating it, and therefore the error shouldn't be detected.

So this is a case where zero-initializing and then calling the constructor is not equivalent to value-initializing, because one case generates an error and the other doesn't.

This is sort of unfortunate, because one doesn't want to generate all the required initializations at the point where the "()" initialization occurs. One would like those initializations to be packaged in a function, and the default constructor is pretty much the function one wants.

I see several implementation choices:

  1. Zero the object, then call the default generated constructor. This is not valid unless the standard is changed to say that the default constructor might be generated for value-initialization cases like the above (that is, it's implementation-dependent whether the constructor definition is generated). The zeroing operation can of course be optimized, if necessary, to hit only the pieces of the object that would otherwise be left uninitialized. An alternative would be to require generation of the constructor for value-initialization cases, even if the implementation technique doesn't call the constructor at that point. It's pretty likely that the constructor is going to have to be generated at some point in the program anyway.
  2. Make a new value-initialization "constructor," whose body looks a lot like the usual generated constructor, but which also zeroes other members. No errors would be generated while generating this modified constructor, because it generates code that does full initialization. (Actually, it wouldn't guarantee initialization of reference members, and that might be an argument for generating the constructor, in order to get that error.) This is standard-conforming, but it destroys object-code compatibility.
  3. Variation on (1): Zero first, and generate the object code for the default constructor when it's needed for value-initialization cases, but don't issue any errors at that time. Issue the errors only if it turns out the constructor is "really" referenced. Aside from the essential shadiness of this approach, I fear that something in the generation of the constructor will cause a template instantiation which will be an abservable side effect.

Personally, I find option 1 the least objectionable.

History
Date User Action Args
2008-10-05 00:00:00adminsetstatus: wp -> cd1
2003-04-25 00:00:00adminsetstatus: dr -> wp
2002-11-08 00:00:00adminsetmessages: + msg774
2002-11-08 00:00:00adminsetstatus: ready -> dr
2002-05-10 00:00:00adminsetstatus: review -> ready
2001-11-09 00:00:00adminsetmessages: + msg554
2001-11-09 00:00:00adminsetstatus: open -> review
2001-07-23 00:00:00admincreate