Purpose of Explicit Default Constructors

Purpose of Explicit Default Constructors

This declares an explicit default constructor:

struct A {
explicit A(int a1 = 0);
};

A a = 0; /* not allowed */
A b; /* allowed */
A c(0); /* allowed */

In case there is no parameter, like in the following example, the explicit is redundant.

struct A {
/* explicit is redundant. */
explicit A();
};

In some C++0x draft (I believe it was n3035), it made a difference in the following way:

A a = {}; /* error! */
A b{}; /* alright */

void function(A a);
void f() { function({}); /* error! */ }

But in the FCD, they changed this (though, I suspect that they didn't have this particular reason in mind) in that all three cases value-initialize the respective object. Value-initialization doesn't do the overload-resolution dance and thus won't fail on explicit constructors.

Purpose of explicitly deleting the default constructor

Any function can be = deleted. A default constructor is a function, so it can be deleted. There's no need to make a language carveout for that.

That some users choose to explicitly delete the default constructor (or the pre-C++ pseudo-equivalent of a private declaration with no definition) when it would not have been generated by the compiler is harmless. Indeed, it has some small benefits.

  1. If someone changes the class to remove its constructors, the class won't suddenly gain a default constructor.

  2. You don't have to remember what the rules are about when a default constructor is generated. For example:

    I guess that’s not including = default

    This proves my point, because you guessed wrong. Explicitly defaulted constructors do count as "user-provided", and thus they do suppress the creation of an implicit default constructor. Having to remember that is bothersome; it's clearer to just state it outright.

Explicit default constructors in C++17

The rationale for the library change is in LWG 2510 "Tag types should not be DefaultConstructible":

std::experimental::optional, for certain reasons, specifies its nullopt type to not be DefaultConstructible. It doesn't do so for its tag type in_place_t and neither does the standard proper for any of its tag types. That turns out to be very unfortunate, consider the following:

#include <memory>
#include <array>

void f(std::array<int, 1>, int) {} // #1
void f(std::allocator_arg_t, int) {} // #2

int main()
{
f({}, 666); // #3
}

The call at #3 is ambiguous. What's even worse is that if the overload #1 is removed, the call works just fine. The whole point of a tag type is that it either needs to mentioned in a call or it needs to be a forwarded argument, so being able to construct a tag type like that makes no sense.

The LWG issue evolved side-by-side with CWG 1518 "Explicit default constructors and copy-list-initialization", which has useful background.

Default initialization explicit constructor c++

Your use is okay. The worst thing that could happen would be that the compiler would not be able to use the constructor since it is explicit and fail to compile. However, defining a variable as you have will correctly call the explicit default constructor.

The use of explicit for a default constructor prevents uses like the following:

Foo some_fn() {
return {}; // Fails as the default constructor is explicit.
return Foo{}; // OK
}

What is the point of a C++ implicit default constructor?

The point of the implicit default constructor is the same point as of any other constructor.

Something needs to construct every instance of a given class. The class instance is not going to appear out of thin air, by itself. Something, somewhere, has the job of constructing the object.

If an explicit constructor is not declared, an implicit default constructor gets automatically defined, that default-constructs the class's superclasses, and any class members.

Here, "default-constructs" also includes the "do nothing" option, if the class member is a primitive type with no explicit constructor. So, in the end, the implicit default constructor may end up doing nothing. But it is still defined, if an explicit constructor is not specified (and if the implicit default constructor is not explicitly deleted, of course).

Implicit vs. Explicit Default Constructor Call

Quotes are from Standard p8.5/1

There is no user-defined constructor here, so, compiler will use the default one. And default constructor will not initialize members with zeros.

If no initializer is specified for an object, the object is
default-initialized.

To default-initialize an object of type T means:

If T is a (possibly cv-qualified) class type (Clause 9), constructors
are considered. The applicable constructors are enumerated (13.3.1.3),
and the best one for the initializer () is chosen through overload
resolution (13.3). The constructor thus selected is called, with an
empty argument list, to initialize the object.

In second case will be value-initialization.

An object whose initializer is an empty set of parentheses, i.e., (),
shall be value-initialized.

To value-initialize an object of type T means:

if T is a (possibly cv-qualified) class type without a user-provided
or deleted default constructor, then the object is zero-initialized
and the semantic constraints for default-initialization are checked,
and if T has a non-trivial default constructor, the object is
default-initialized;

To zero-initialize an object or reference of type T means:

if T is a (possibly cv-qualified) non-union class type, each
non-static data member and each base-class subobject is
zero-initialized and padding is initialized to zero bits;



Related Topics



Leave a reply



Submit