c++c++17default-constructorexplicit-constructor

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


Solution

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