2

I know that default initialization for non-POD types will also default initialize non-static non-POD member variables by calling their default constructor. But I'm not sure exactly how this happens. Here is an example of what I mean:

#include <iostream>
#include <vector>
using namespace std;

class Test2 {
  public:
    Test2() {cout <<"Here";}
};

class Test {
  public:
    Test() {}
    Test2 i;
};

int main() {
  Test foo;
}

The output is:

Here

Based on the C++ standard on initializers (8.5), for default initialization:

— if T is a non-POD class type (clause 9), the default constructor
for T is called (and the initialization is ill-formed if T has no
accessible default constructor);

So given this, I do expect that the default constructor Test() will get called, but my empty default constructor for the class Test does not initialize Test2 i explicitly yet clearly, Test2() is getting called implicitly somehow. What I'm wondering is how this happens?

Similarly, for value initialization (not related to example above), if an empty user defined default constructor does not explicitly zero initialize a POD non-static member variable, how does that variable get zero initialized (which I know it does do)? Since based on the standard, it seems that for value initialization, all that happens when you have a user defined default constructor is that the constructor gets called.

The corresponding part of the C++ standard for value initialization is the following:

— if T is a class type (clause 9) with a user-declared constructor (12.1), then the   
default constructor for T is called (and the initialization is ill-formed if T has no 
accessible default constructor);

This question is similar to c++ empty constructor and member initialization But the difference is that instead of asking what the end result behavior is, I'd like to know why the end result behavior happens.

Community
  • 1
  • 1
user1082160
  • 195
  • 1
  • 1
  • 8
  • The compiler is written to follow the rules as layed out by the standard. The standard says X should happen in situation Y, so the compiler emits code to perform X in situation Y. I don't understand how this can be confusing. – Benjamin Lindley Jul 28 '13 at 05:30
  • if you don't initialize it in the constructor its initialized for ou – A. H. Jul 28 '13 at 05:43
  • @Benjamin: My confusion is that I'm seeing X should happen in situation Y with side effects, where side effects is not explained by the standard that I listed above. – user1082160 Jul 28 '13 at 05:55
  • You quoted the answer in the question, I think you just failed to apply it recursively. "if T is a non-POD class type (clause 9), the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor);" – kfsone Jul 28 '13 at 07:15

3 Answers3

2

In the C++11 standard, section 12.6 paragraph 8:

In a non-delegating constructor, if a given non-static data member or base class is not designated by a mem-initializer-id (including the case where there is no mem-initializer-list because the constructor has no ctor-initializer) and the entity is not a virtual base class of an abstract class (10.4), then

  • if the entity is a non-static data member that has a brace-or-equal-initializer, the entity is initialized as specified in 8.5;
  • otherwise, if the entity is a variant member (9.5), no initialization is performed;
  • otherwise, the entity is default-initialized (8.5).

You are encountering the third case, where there is no initializer for the member and the member isn't a variant member, so in that case it is default-initialized.

Also, from paragraph 10:

In a non-delegating constructor, initialization proceeds in the following order: - First, and only for the constructor of the most derived class (1.8), virtual base classes are initialized in the order they appear on a depth-first left-to-right traversal of the directed acyclic graph of base classes, where “left-to-right” is the order of appearance of the base classes in the derived class base-specifier-list.

  • Then, direct base classes are initialized in declaration order as they appear in the base-specifier-list (regardless of the order of the mem-initializers).
  • Then, non-static data members are initialized in the order they were declared in the class definition (again regardless of the order of the mem-initializers).
  • Finally, the compound-statement of the constructor body is executed.

Regardless of what you specify in your constructor, the members are going to be initialized just before the body of the constructor is executed.

A mem-initializer-id is the identifier used to refer to a member in a constructor initializer list:

class Test {
  public:
    Test() : i() {} // Here `i` is a mem-initializer-id
    Test2 i;
};
Vaughn Cato
  • 63,448
  • 5
  • 82
  • 132
  • Ah i see, I didn't know about this part of the standard. Just curious, what is a mem-initializer-id? – user1082160 Jul 28 '13 at 06:01
  • 1
    @user1082160: I added some explanation to my answer. – Vaughn Cato Jul 28 '13 at 06:05
  • One last question, I noticed from the standard u posted (12.6 paragraph 8), it doesn't really mention how for value initialization, non-static pod types get zero initialized even if the user defined empty default constructor does not initialize the pod type. From the standard, I would assume the pod types would get default initialized which means it's not initialized. – user1082160 Jul 28 '13 at 06:47
  • @user1082160 §8.5/7 defines *value-initialization*. The second bullet point: "if `T` is a (possibly cv-qualified) non-union class type without a user-provided constructor, then the object is zero-initialized and, if `T`’s implicitly-declared default constructor is non-trivial, that constructor is called." *sounds* like what you are talking about, but note that you've reversed the sense. That rule applies only if there is no user-provided constructor. – Casey Jul 28 '13 at 09:12
  • @Casey: The scenario I'm curious about is that say I have a non-pod class that has a non-static pod member variable and my user-defined default constructor does not explicitly initialize this member variable. Based on the definition of value-initialization defined in §8.5/7, the first bullet point: "if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor)" should be called. – user1082160 Jul 28 '13 at 18:13
  • Then based on 12.6 paragraph 8, the non-static pod member variable should be default-initialized. But we know for value initialization, the variable is actually zero-initialized. Why does this happen?? – user1082160 Jul 28 '13 at 18:14
  • 1
    @user1082160 You are mistaken. If you value-initialize a class with a default constructor, no initialization is done for non-static POD members unless they are explicitly initialized in the constructor. e.g. [this program, which technically has undefined behavor](http://coliru.stacked-crooked.com/view?id=47f33b7aa7d96fa244ca240673864c33-e1204655eaff68246b392dc70c5a32c9). – Casey Jul 28 '13 at 18:23
  • Wow! Didn't know about this behavior. – user1082160 Jul 28 '13 at 18:35
2

Value initialization of a type with a user-defined default constructor performs no initialization on non-static POD members if they are not explicitly initialized in the constructor. E.g., in this program:

#include <iostream>

using namespace std;

struct Foo {
    // Foo has a user-defined default constructor
    // that does not initialize i
    Foo() {}
    int i;
};

int main() {
    Foo x{}; // value-initialize x
    cout << x.i << endl;
}

x.i is uninitialized. The program therefore technically has undefined behavior, but in this case "undefined behavior" most likely means that it will print an unspecified integer value that is likely not 0.

Language lawyer argument:

  • §12.6.1p2: "An object of class type can also be initialized by a braced-init-list. List-initialization semantics apply; see 8.5 and 8.5.4."
  • §8.5.4p3: "List-initialization of an object or reference of type T is defined as follows: ... If the initializer list has no elements and T is a class type with a default constructor, the object is value-initialized."
  • §8.5p7: "To value-initialize an object of type T means: ... if T is a (possibly cv-qualified) class type (Clause 9) with a user-provided constructor (12.1), then the default constructor for T is called (and the initialization is ill-formed if T has no accessible default constructor)
Casey
  • 41,449
  • 7
  • 95
  • 125
0

According to the draft of the C++ standard found at http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1905.pdf, section 12.6.2:

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

— If the entity is a non-static data member of (possibly cv-qualified) class type (or array thereof) or a base class, and the entity class is a non-POD class, the entity is default-initialized (8.5). If the entity is a non-static data member of a const-qualified type, the entity class shall have a user-declared default constructor.

— Otherwise, the entity is not initialized. If the entity is of const-qualified type or reference type, or of a (possibly cv-qualified) POD class type (or array thereof) containing (directly or indirectly) a member of a const-qualified type, the program is ill-formed.

In other words, if an object of a non-POD class type does not appear in an initializer list, the compiler interprets this as if the object had appeared with its default constructor being called.

Also, note that other types (i.e. primitives and POD types) are not initialized, which is different from what you indicated in your question. Global objects are zero-initialized, but the same isn't true for objects on the stack. Here is a small program I put together to test this:

#include <iostream>

class T
{
public:
    T() {}

    void put(std::ostream &out)
    {
        out << "a = " << a << std::endl;
        out << "b = " << b << std::endl;
        out << "c = " << c << std::endl;
    }
private:
    int a;
    int b;
    int c;
};

T t2;

int main()
{
    T t;
    t.put(std::cout);
    t2.put(std::cout);

    return 0;
}

Compiling with g++ 4.5.2, I got the following output:

a = 8601256
b = 3
c = 2130567168
a = 0
b = 0
c = 0
Community
  • 1
  • 1
Ben S.
  • 1,133
  • 7
  • 7
  • I mentioned value initialization does zero initialize pod types, not default initialization. Here u are default initializing T which is why the pod types are not initialized. – user1082160 Jul 28 '13 at 05:59