DefaultConstructible
should require default initializationSection: 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 variablet
: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
26.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]:
Modify [concept.defaultconstructible] as follows:
template<class T> concept DefaultConstructible = Constructible<T> && see below;-?- Type
T
modelsDefaultConstructible
only if the variable definitionis well-formed for some invented variableT t;t
. Access checking is performed as if in a context unrelated toT
. 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.
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>
istrue
if and only if the variable definitionis well-formed for some invented variableT t;t
; otherwise it isfalse
. Access checking is performed as if in a context unrelated toT
. 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.
Modify 18.3 [concepts.syn], header
<concepts>
synopsis, as indicated:[…] // [concept.defaultconstructible], concept default_constructibleinitializable template<class T> concept default_constructibleinitializable = see below; […]Modify [concept.defaultconstructible] as indicated:
18.4.12 Concept
default_
[concept.defaultconstructibleinitializableconstructibleinitializable]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>
istrue
if and only if the variable definitionis well-formed for some invented variableT t;t
; otherwise it isfalse
. Access checking is performed as if in a context unrelated toT
. Only the validity of the immediate context of the variable initialization is considered.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>; […]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); } […]Modify 26.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:
[…]Modify 26.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:
[…]Modify [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 likeoptional<T>
with the following differences:
(1.1) — […]
(1.2) — If
T
modelsdefault_
, the default constructor ofconstructibleinitializablesemiregular-box<T>
is equivalent to:constexpr semiregular-box() noexcept(is_nothrow_default_constructible_v<T>) : semiregular-box{in_place} { }(1.3) — […]
Modify 25.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.
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>
istrue
if and only if the variable definitionis well-formed for some invented variableT t;t
; otherwise it isfalse
. Access checking is performed as if in a context unrelated toT
. Only the validity of the immediate context of the variable initialization is considered.