How do I make user defined empty default constructor behave like compiler defined empty constructor

Here's the basic idea. Both A a2{}; and B b2{}; will perform what is called "value initialization" on the two objects. However, the way value initialization behaves depends on how those types are defined.

B is an object which has a user-provided default constructor. "User-provided" being the term for when you provide a body for the default constructor. Because of that, value initialization will call the default constructor. That default constructor does not initialize its members, so the members remain uninitialized.

A is an object which does not have a user-provided default constructor. Nor does it have any other user-provided constructors. And the default constructor is not deleted either. And there are no default member initializers in A. Given all of that, value initialization will perform zero initialization on the object. Which means that it will write all zeros to the memory for that object before it comes into existence.

That's what the rules say; the two do not behave the same, nor are they meant to. Nor is there anything you can do to make a user-provided default constructor act like a defaulted default constructor in all cases. You can make the user-provided constructor value initialize its members, but it would do so all the time, even if you use default initialization (B b1;, for example).

Why do the rules say that? Because = default is not supposed to be equivalent to an empty constructor body. Indeed, being different is why = default exists as a feature.

When you = default your default constructor, you are saying "generate the default constructor as you normally would". This is important, because there are things you can do which actively prevent the compiler from generating a default constructor for you. If you specify other constructors (which are not copy/move constructors), the compiler will not automatically generate one. So by using = default syntax, you're telling the compiler that you want the generated default constructor.

By contrast, if you make an empty body in your default constructor, you are saying something totally different. You are explicitly saying, "If a user calls my default constructor, I want my members to be default-initialized." That's what it means when you have an empty member initializer list in a constructor, after all. So that's what it should do.

If = default and an empty body behaved the same, there would be no way for you to get that behavior, to say that you want default initialization of your members no matter what.

Basically, Cppreference's statement is completely wrong; it does not have "exactly the same effect as a user-defined constructor with empty body and empty initializer list". Nor is it supposed to.


If you want to understand the thinking of value initialization a bit further, consider this.

int i{};

That is guaranteed to produce a value of 0 for i. It is therefore reasonable that this:

struct S{int i;};
S s{};

Should also produce a value of 0 for s.i. How does that happen? Because value initialization will zero-initialize s.

So how does a user say that they don't want that, or want some special form of initialization? You communicate that the same way you communicate everything else: you add a constructor. Specifically, a default constructor that does the form of initialization you want.


If any constructor is provided zero-initialization does not take place as stated here

Zero initialization is performed in the following situations:

...

2) As part of value-initialization sequence for non-class types and for members of value-initialized class types that have no constructors, including value initialization of elements of aggregates for which no initializers are provided.

Also here

The effects of value initialization are:

1) if T is a class type with at least one user-provided constructor of any kind, the default constructor is called;

Which makes sense. One should be able to control whether any additional operations can be added. If you provide a constructor, you take responsibility for your object initializations.