Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Explicit default constructors in C++17

In C++17, empty tag types in the standard library now have default constructors which are marked explicit, and are also = default. For example, std::piecewise_construct_t is now defined as

struct piecewise_construct_t { explicit piecewise_construct_t() = default; };

My question is simply, what is the reason for this change from C++14? What does an explicitly defaulted explicit default constructor (!) mean for an empty class?

(To avoid being marked as a dupe: this question from 2010 asks about the purpose of explicit default constructors, but that was pre-C++11 and a long time ago now so things have likely changed. This question is more recent, but the answer seems to suggest that aggregate initialization will be performed regardless of the presence of the defaulted constructor, so I'm curious as to the reason for this change in the latest standard.)

like image 675
Tristan Brindle Avatar asked May 24 '17 23:05

Tristan Brindle


People also ask

Can we call a default constructor explicitly?

Yes, it is possible to call special member functions explicitly by the programmer.

How do you initialize an explicit constructor?

There are two ways to initialize a class object: Using a parenthesized expression list. The compiler calls the constructor of the class using this list as the constructor's argument list. Using a single initialization value and the = operator.

What is implicit default constructor?

A default constructor is a constructor that either has no parameters, or if it has parameters, all the parameters have default values. If no user-defined constructor exists for a class A and one is needed, the compiler implicitly declares a default parameterless constructor A::A() .

What are the 3 types of constructor?

Constructors in C++ are the member functions that get invoked when an object of a class is created. There are mainly 3 types of constructors in C++, Default, Parameterized and Copy constructors.


1 Answers

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.

like image 118
Casey Avatar answered Sep 23 '22 17:09

Casey