This page is a snapshot from the LWG issues list, see the Library Active Issues List for more information and the meaning of C++17 status.

### 2510. Tag types should not be `DefaultConstructible`

Section: 17.7 [support.dynamic], 22.2 [utility], 22.3.5 [pair.piecewise], 20.2.2 [memory.syn], 20.2.7 [allocator.tag], 33.6 [thread.mutex] Status: C++17 Submitter: Ville Voutilainen Opened: 2015-06-13 Last modified: 2017-07-30

Priority: 2

View all other issues in [support.dynamic].

View all issues with C++17 status.

Discussion:

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

Making the types have an explicit default constructor might have helped, but CWG 1518 is going against that idea.

[optional.nullopt]/3 solves this problem for `nullopt`:

> Type `nullopt_t` shall not have a default constructor. It shall be a literal type. Constant `nullopt` shall be initialized with an argument of literal type.

[2015-06, Telecon]

Move to Tentatively Ready.

[2015-10, Kona Saturday afternoon]

Move back to Open

JW: The linked Core issue (CWG 1518) gives us a better tool to solve this (explicit default constructors). [The CWG Issue means that an explicit default constructor will no longer match "{}".] JW explains that it's important that tag types cannot be constructed from "{}" (e.g. the allocator tag in the tuple constructors).

WEB: Should we now go back and update our constructors? JW: For tag types, yes.

VV: The guideline is that anything that does not mention the type name explicitly should not invoke an explicit constructor.

Ville will provide wording.

Discussion about pair/tuple's default constructor - should they now be explicit?

[2016-01-31]

Ville provides revised wording.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4527.

> 1. In 17.7 [support.dynamic]/1, change the header `<new>` synopsis:

>    >     […]
>    >     struct nothrow_t {}; see below
>    >     extern const nothrow_t nothrow;
>    >     […]

> 2. Add a new paragraph after 17.7 [support.dynamic]/1 (following the header `<new>` synopsis):

>    > -?- Type `nothrow_t` shall not have a default constructor.

> 3. In 22.2 [utility]/2, change the header `<utility>` synopsis:

>    >     […]
>    >     // 20.3.5, pair piecewise construction
>    >     struct piecewise_construct_t { }; see below
>    >     constexpr piecewise_construct_t piecewise_construct{ unspecified };
>    >     […]

> 4. Add a new paragraph after 22.2 [utility]/2 (following the header `<utility>` synopsis):

>    > -?- Type `piecewise_construct_t` shall not have a default constructor. It shall be a literal type. Constant `piecewise_construct` shall be initialized with an argument of literal type.

> 5. In 22.3.5 [pair.piecewise], apply the following edits:

>    >     struct piecewise_construct_t { };
>    >     constexpr piecewise_construct_t piecewise_construct{ unspecified };

> 6. In 20.2.2 [memory.syn]/1, change the header `<memory>` synopsis:

>    >     […]
>    >     // 20.7.6, allocator argument tag
>    >     struct allocator_arg_t { }; see below
>    >     constexpr allocator_arg_t allocator_arg{ unspecified };
>    >     […]

> 7. Add a new paragraph after 20.2.2 [memory.syn]/1 (following the header `<memory>` synopsis):

>    > -?- Type `allocator_arg_t` shall not have a default constructor. It shall be a literal type. Constant `allocator_arg` shall be initialized with an argument of literal type.

> 8. In 20.2.7 [allocator.tag], apply the following edits:

>    >     namespace std {
>    >       struct allocator_arg_t { };
>    >       constexpr allocator_arg_t allocator_arg{ unspecified };
>    >     }

>    > Editorial drive-by: `piecewise_construct_t` is written, in 22.3.5 [pair.piecewise] like

>    > >     struct piecewise_construct_t { };
>    > >     constexpr piecewise_construct_t piecewise_construct{};

>    > whereas other tag types such as `allocator_construct_t` are, in e.g. 20.2.7 [allocator.tag], written like

>    > >     namespace std {
>    > >       struct allocator_arg_t { };
>    > >       constexpr allocator_arg_t allocator_arg{};
>    > >     }

>    > We should decide whether or not to write out the `std` namespace in such paragraphs. I would suggest not to write it out.

> 9. In 33.6 [thread.mutex]/1, change the header `<mutex>` synopsis:

>    >     […]
>    >     struct defer_lock_t { }; see below
>    >     struct try_to_lock_t { }; see below
>    >     struct adopt_lock_t { }; see below

>    >     constexpr defer_lock_t defer_lock { unspecified  };
>    >     constexpr try_to_lock_t try_to_lock { unspecified  };
>    >     constexpr adopt_lock_t adopt_lock { unspecified  };
>    >     […]

> 10. Add three new paragraphs after [thread.mutex]/1 (following the header `<mutex>` synopsis):

>     > -?- Type `defer_lock_t` shall not have a default constructor. It shall be a literal type. Constant `defer_lock` shall be initialized with an argument of literal type.

>     > -?- Type `try_to_lock_t` shall not have a default constructor. It shall be a literal type. Constant `try_to_lock` shall be initialized with an argument of literal type.

>     > -?- Type `adopt_lock_t` shall not have a default constructor. It shall be a literal type. Constant `adopt_lock` shall be initialized with an argument of literal type.

[2016-03 Jacksonville]

AM: should have note about compatibility in Annex C
HH: like this idiom well enough that I've started using it in my own code
AM: why are pair and tuple involved here?
GR: they are the only types which forward explicitness with EXPLICIT
AM: British spelling of behaviour
AM: happy to drop my issue about Annex C

[2016-06 Oulu]

This is waiting on Core issue 1518

Saturday: Core 1518 was resolved in Oulu

[2016-07 Chicago]

This is related to 2736

Monday PM: Moved to Tentatively Ready

Proposed resolution:

This wording is relative to N4567.

1. In 17.7 [support.dynamic]/1, change the header `<new>` synopsis:

   >     […]
   >     struct nothrow_t { explicit nothrow_t() = default; };
   >     extern const nothrow_t nothrow;
   >     […]

2. In 22.2 [utility]/2, change the header `<utility>` synopsis:

   >     […]
   >     // 20.3.5, pair piecewise construction
   >     struct piecewise_construct_t { explicit piecewise_construct_t() = default; };
   >     constexpr piecewise_construct_t piecewise_construct{};
   >     […]

3. In 22.3.2 [pairs.pair], change the class template `pair` synopsis:

   >     […]
   >     pair(pair&&) = default;
   >     EXPLICIT constexpr pair();
   >     EXPLICIT constexpr pair(const T1& x, const T2& y);
   >     […]

4. Around 22.3.2 [pairs.pair] p3, apply the following edits:

   >     EXPLICIT constexpr pair();

   > > -3- Effects: Value-initializes `first` and `second`.

   > > -4- Remarks: This constructor shall not participate in overload resolution unless `is_default_constructible<first_type>::value` is `true` and `is_default_constructible<second_type>::value` is `true`. [Note: This behaviour can be implemented by a constructor template with default template arguments. — end note] The constructor is explicit if and only if either `first_type` or `second_type` is not implicitly default-constructible. [Note: This behaviour can be implemented with a trait that checks whether a `const first_type&` or a `const second_type&` can be initialized with `{}`. — end note]

5. In 22.3.5 [pair.piecewise], apply the following edits:

   >     struct piecewise_construct_t { explicit piecewise_construct_t() = default; };
   >     constexpr piecewise_construct_t piecewise_construct{};

6. In 22.4.4 [tuple.tuple], change the class template `tuple` synopsis:

   >     […]
   >     // 20.4.2.1, tuple construction
   >     EXPLICIT constexpr tuple();
   >     EXPLICIT constexpr tuple(const Types&...); // only if sizeof...(Types) >= 1
   >     […]

7. Around 22.4.4.1 [tuple.cnstr] p4, apply the following edits:

   >     EXPLICIT constexpr tuple();

   > > -4- Effects: Value initializes each element.

   > > -5- Remarks: This constructor shall not participate in overload resolution unless `is_default_constructible<Ti>::value` is `true` for all i. [Note: This behaviour can be implemented by a constructor template with default template arguments. — end note] The constructor is explicit if and only if `Ti` is not implicitly default-constructible for at least one i. [Note: This behaviour can be implemented with a trait that checks whether a `const Ti&` can be initialized with `{}`. — end note]

8. In 20.2.2 [memory.syn]/1, change the header `<memory>` synopsis:

   >     […]
   >     // 20.7.6, allocator argument tag
   >     struct allocator_arg_t { explicit allocator_arg_t() = default; };
   >     constexpr allocator_arg_t allocator_arg{};
   >     […]

9. In 20.2.7 [allocator.tag], apply the following edits:

   >     namespace std {
   >       struct allocator_arg_t { explicit allocator_arg_t() = default; };
   >       constexpr allocator_arg_t allocator_arg{};
   >     }

   > Editorial drive-by: `piecewise_construct_t` is written, in 22.3.5 [pair.piecewise] like

   > >     struct piecewise_construct_t { };
   > >     constexpr piecewise_construct_t piecewise_construct{};

   > whereas other tag types such as `allocator_construct_t` are, in e.g. 20.2.7 [allocator.tag], written like

   > >     namespace std {
   > >       struct allocator_arg_t { };
   > >       constexpr allocator_arg_t allocator_arg{};
   > >     }

   > We should decide whether or not to write out the `std` namespace in such paragraphs. I would suggest not to write it out.

10. In 33.6 [thread.mutex]/1, change the header `<mutex>` synopsis:

    >     […]
    >     struct defer_lock_t { explicit defer_lock_t() = default; };
    >     struct try_to_lock_t { explicit try_to_lock_t() = default; };
    >     struct adopt_lock_t { explicit adopt_lock_t() = default; };

    >     constexpr defer_lock_t defer_lock { };
    >     constexpr try_to_lock_t try_to_lock { };
    >     constexpr adopt_lock_t adopt_lock { };
    >     […]