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

### 3149. `DefaultConstructible` should require default initialization

Section: 18.4.12 [concept.default.init] Status: C++20 Submitter: Casey Carter Opened: 2018-08-09 Last modified: 2021-06-06

Priority: 2

View all other issues in [concept.default.init].

View all issues with C++20 status.

Discussion:

`DefaultConstructible<T>` is equivalent to `Constructible<T>` (18.4.11 [concept.constructible]), which is equivalent to `is_constructible_v<T>` (21.3.5.4 [meta.unary.prop]). Per 21.3.5.4 [meta.unary.prop] paragraph 8:

> The predicate condition for a template specialization `is_constructible<T, Args...>` shall be satisfied if and only if the following variable definition would be well-formed for some invented variable `t`:

> >     T t(declval<Args>()...);

`DefaultConstructible<T>` requires that objects of type `T` can be value-initialized, rather than default-initialized as intended.

The library needs a constraint that requires object types to be default-initializable: the "rangified" versions of the algorithms in 27.11.3 [uninitialized.construct.default] proposed in P0896 "The One Ranges Proposal", for example. Users will also want a mechanism to provide such a constraint, and they're likely to choose `DefaultConstructible` despite its subtle unsuitability.

There are two alternative solutions: (1) change `DefaultConstructible` to require default-initialization, (2) change `is_default_constructible_v` to require default-initializaton and specify the concept in terms of the trait. (2) is probably too breaking a change to be feasible.

[2018-08-20 Priority set to 2 after reflector discussion]

Previous resolution [SUPERSEDED]:

> 1. Modify [concept.defaultconstructible] as follows:

>    >     template<class T>
>    >       concept DefaultConstructible = Constructible<T> && see below;

>    > -?- Type `T` models `DefaultConstructible` only if the variable definition

>    > >     T t;

>    > is well-formed for some invented variable `t`. Access checking is performed as if in a context unrelated to `T`. Only the validity of the immediate context of the variable initialization is considered.

[2018-08-23 Tim provides updated P/R based on Batavia discussion]

[2018-10-28 Casey expands the problem statement and the P/R]

During Batavia review of P0896R3, Tim Song noted that `{}` is not necessarily a valid initializer for a `DefaultConstructible` type. In this sample program (see Compiler Explorer):

>     struct S0 { explicit S0() = default; };
>     struct S1 { S0 x; }; // Note: aggregate
>     S1 x;   // Ok
>     S1 y{}; // ill-formed; copy-list-initializes x from {}

`S1` can be default-initialized, but not list-initialized from an empty braced-init-list. The consensus among those present was that `DefaultConstructible` should prohibit this class of pathological types by requiring that initialization form to be valid.

[2019 Cologne Wednesday night]

Status to Ready

[2019-10-07 Casey rebases P/R onto N4830 and incorporates WG21-approved changes from P1754R1]

Previous resolution [SUPERSEDED]:

> This wording is relative to N4762.

> 1. Modify [concept.defaultconstructible] as follows:

>    >     template<class T>
>    >       inline constexpr bool is-default-initializable = see below; // exposition only

>    >     template<class T>
>    >       concept DefaultConstructible = Constructible<T> && requires { T{}; } && is-default-initializable<T>;

>    > -?- For a type `T`, `is-default-initializable<T>` is `true` if and only if the variable definition

>    > >     T t;

>    > is well-formed for some invented variable `t`; otherwise it is `false`. Access checking is performed as if in a context unrelated to `T`. Only the validity of the immediate context of the variable initialization is considered.

P1754R1 "Rename concepts to `standard_case` for C++20" - as approved by both LEWG and LWG in Cologne - contained instructions to rename the `DefaultConstructible` concept to `default_initializable` "If LWG 3151 is accepted." 3151 is the unrelated "`ConvertibleTo` rejects conversion from array and function types"; this issue is intended by P1754R1. Since P1754R1 was applied to the working draft in Cologne, whereas this issue was only made Ready, we should apply the desired renaming to the P/R of this issue.

Previous resolution [SUPERSEDED]:

> This wording is relative to N4830.

> 1. Modify 18.3 [concepts.syn], header `<concepts>` synopsis, as indicated:

>    >     […]
>    >     //  [concept.defaultconstructible], concept default_constructibleinitializable
>    >     template<class T>
>    >     concept default_constructibleinitializable = see below;
>    >     […]

> 2. Modify [concept.defaultconstructible] as indicated:

>    > 18.4.12 Concept `default_constructibleinitializable` [concept.defaultconstructible initializable]

>    >     template<class T>
>    >       inline constexpr bool is-default-initializable = see below; // exposition only

>    >     template<class T>
>    >       concept default_constructibleinitializable = constructible_from<T> && requires { T{}; } && is-default-initializable<T>;

>    > -?- For a type `T`, `is-default-initializable<T>` is `true` if and only if the variable definition

>    > >     T t;

>    > is well-formed for some invented variable `t`; otherwise it is `false`. Access checking is performed as if in a context unrelated to `T`. Only the validity of the immediate context of the variable initialization is considered.

> 3. Modify 18.6 [concepts.object] as indicated:

>    > -1- This subclause describes concepts that specify the basis of the value-oriented programming style on which the library is based.

>    >     template<class T>
>    >     concept movable = is_object_v<T> && move_constructible<T> &&
>    >     […]
>    >     template<class T>
>    >     concept semiregular = copyable<T> && default_constructibleinitializable<T>;
>    >     […]

> 4. Modify 20.2.2 [memory.syn], header `<memory>` synopsis, as indicated:

>    >     […]
>    >     namespace ranges {
>    >       template<no-throw-forward-iterator I, no-throw-sentinel<I> S>
>    >         requires default_constructibleinitializable<iter_value_t<I>>
>    >           I uninitialized_default_construct(I first, S last);
>    >       template<no-throw-forward-range R>
>    >         requires default_constructibleinitializable<range_value_t<R>>
>    >           safe_iterator_t<R> uninitialized_default_construct(R&& r);

>    >       template<no-throw-forward-iterator I>
>    >         requires default_constructibleinitializable<iter_value_t<I>>
>    >           I uninitialized_default_construct_n(I first, iter_difference_t<I> n);
>    >     }
>    >     […]
>    >     namespace ranges {
>    >       template<no-throw-forward-iterator I, no-throw-sentinel<I> S>
>    >         requires default_constructibleinitializable<iter_value_t<I>>
>    >          I uninitialized_value_construct(I first, S last);
>    >       template<no-throw-forward-range R>
>    >         requires default_constructibleinitializable<range_value_t<R>>
>    >           safe_iterator_t<R> uninitialized_value_construct(R&& r);

>    >       template<no-throw-forward-iterator I>
>    >         requires default_constructibleinitializable<iter_value_t<I>>
>    >           I uninitialized_value_construct_n(I first, iter_difference_t<I> n);
>    >     }
>    >     […]

> 5. Modify 27.11.3 [uninitialized.construct.default] as indicated:

>    >     namespace ranges {
>    >       template<no-throw-forward-iterator I, no-throw-sentinel<I> S>
>    >         requires default_constructibleinitializable<iter_value_t<I>>
>    >           I uninitialized_default_construct(I first, S last);
>    >       template<no-throw-forward-range R>
>    >         requires default_constructibleinitializable<range_value_t<R>>
>    >           safe_iterator_t<R> uninitialized_default_construct(Ramp;& r);
>    >     }

>    > > -2- Effects: Equivalent to:

>    > > […]

>    >     namespace ranges {
>    >       template<no-throw-forward-iterator I>
>    >         requires default_constructibleinitializable<iter_value_t<I>>
>    >           I uninitialized_default_construct_n(I first, iter_difference_t<I> n);
>    >     }

>    > > -4- Effects: Equivalent to:

>    > > […]

> 6. Modify 27.11.4 [uninitialized.construct.value] as indicated:

>    >     namespace ranges {
>    >       template<no-throw-forward-iterator I, no-throw-sentinel<I> S>
>    >         requires default_constructibleinitializable<iter_value_t<I>>
>    >           I uninitialized_value_construct(I first, S last);
>    >       template<no-throw-forward-range R>
>    >         requires default_constructibleinitializable<range_value_t<R>>
>    >           safe_iterator_t<R> uninitialized_value_construct(R&& r);
>    >     }

>    > > -2- Effects: Equivalent to:

>    > > […]

>    >     namespace ranges {
>    >       template<no-throw-forward-iterator I>
>    >         requires default_constructibleinitializable<iter_value_t<I>>
>    >           I uninitialized_value_construct_n(I first, iter_difference_t<I> n);
>    >     }

>    > > -4- Effects: Equivalent to:

>    > > […]

> 7. Modify 99 [range.semi.wrap] as indicated:

>    > -1- Many types in this subclause are specified in terms of an exposition-only class template `semiregular-box`. `semiregular-box<T>` behaves exactly like `optional<T>` with the following differences:

>    > 1. (1.1) — […]

>    > 2. (1.2) — If `T` models `default_constructibleinitializable`, the default constructor of `semiregular-box<T>` is equivalent to:

>    >    >     constexpr semiregular-box() noexcept(is_nothrow_default_constructible_v<T>)
>    >    >       : semiregular-box{in_place}
>    >    >     { }

>    > 3. (1.3) — […]

> 8. Modify 26.6.6.2 [range.istream.view], Class template `basic_istream_view` synopsis, as indicated:

>    >     namespace std::ranges {
>    >       […]
>    >       template<movable Val, class CharT, class Traits>
>    >         requires default_constructibleinitializable<Val> &&
>    >           stream-extractable<Val, CharT, Traits>
>    >       class basic_istream_view : public view_interface<basic_istream_view<Val, CharT, Traits>> {
>    >         […]
>    >       }
>    >       […]
>    >     }

[2019-11-17; Daniel comments and restores wording]

During the Belfast 2019 meeting the concept renaming was not voted in by this issue, but separately, the accepted wording can be found in P1917R0#3149. To prevent confusion, the here presented proposed wording has been synchronized with that of the voted in document.

Proposed resolution:

This wording is relative to N4762.

1. Modify [concept.defaultconstructible] as follows:

   >     template<class T>
   >       inline constexpr bool is-default-initializable = see below; // exposition only

   >     template<class T>
   >       concept DefaultConstructible = Constructible<T> && requires { T{}; } && is-default-initializable<T>;

   > -?- For a type `T`, `is-default-initializable<T>` is `true` if and only if the variable definition

   > >     T t;

   > is well-formed for some invented variable `t`; otherwise it is `false`. Access checking is performed as if in a context unrelated to `T`. Only the validity of the immediate context of the variable initialization is considered.