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.)
Yes, it is possible to call special member functions explicitly by the programmer.
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.
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() .
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.
The rationale for the library change is in LWG 2510 "Tag types should not be DefaultConstructible
":
std::experimental::optional
, for certain reasons, specifies itsnullopt
type to not beDefaultConstructible
. It doesn't do so for its tag typein_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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With