3
votes

$8.5/7 states that

— 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.

I am unable to appreciate the last part of this statement "if T’s implicitly-declared default constructor is non-trivial, that constructor is called."

Can someone please explain this with an example?

class A
{
    int x;
};

class B : A {};

B b{};

I think B in the code above is having a non-trivial constructor. But how do I observe call to B's implicitly declared constructor and make sure that my compiler is calling it?

3
Why do you think B has a non-trivial constructor? It meets all the conditions for a trivial constructor given in C++11 12.1/5. - Mike Seymour

3 Answers

6
votes

I think B in the code above is having a non-trivial constructor.

In your example, the constructor is trivial.

Looking at the conditions in C++11 12.1/5, neither class has a user-declared constructor, virtual functions, virtual base classes, members with initialisers, or members of class type; A has no base classes and B only has a trivial base class.

But how do I observe call to B's implicitly declared constructor and make sure that my compiler is calling it?

One way to make a class with an implicit, but non-trivial, default constructor is to have a non-trivial member or base class:

struct A {
    // A user-declared constructor is non-trivial
    A() {std::cout << "Construct A\n";}
};

struct B : A {};

Now you can (indirectly) observe the implicit constructor of B being called, by observing the side-effect when it calls the constructor of A.

2
votes

Explanation after N3797:

A function is user-provided if it is user-declared and not explicitly defaulted or deleted on its first declaration.

Therefore, since you don't declare a default constructor for B, it is not user-provided. The following then applies:

A default constructor is trivial if it is not user-provided and if:

— its class has no virtual functions (10.3) and no virtual base classes (10.1), and

— no non-static data member of its class has a brace-or-equal-initializer, and

— all the direct base classes of its class have trivial default constructors, and

— 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.

So it is indeed trivial, since we can apply the same procedure recursively for A.

Other examples:

struct A { A() = default; }; // Trivial default constructor!
struct A { A() = delete; }; // Also trivial!
struct A { A(); }; // Can't be trivial!

struct B { virtual void f(); }
struct A : B {}; // Non-trivial default constructor.

struct B {};
struct A : virtual B {}; // Non-trivial default constructor.
0
votes

Imagine you have this one

struct A {
  string a;
  int value;
};

int main() {
  A a = A();
  return a.value;
}

a.value is zero when we return that value, because the object was zero-initialized. But that is not enough, because a also contains a string member which has a constructor. For that constructor to be called, the standard arranges that A's constructor is called, which will eventually lead to constructing the member.