C++20 adds a concept called std::default_initializable
. The difference in naming compared to std::is_default_constructible
seems to be way too obvious to be accidental.
Their specifications are worded differently as well (at least on cppreference), however I don't understand what the effective differences are.
Here's a short summary of the cppreference articles:
std::is_default_constructible<T>
requires T foo();
to be well-formed, with the assumption that it's not parsed as a function declaration. In other words (if I understand correctly), T
needs to be destructible andT()
needs to be well-formed.
(edit: it doesn't seem to require destructibility, so here's the first difference)
std::default_initializable
, on the other hand, requires both T foo;
, T()
and T{}
to be well-formed.
Are there any effective differences between those two?
As far as I know, T{}
is always interpreted as T()
(ignoring most vexing parse, which doesn't count here), so I'm not sure why it's mentioned explicitly. Also T()
being well-formed seems to imply T foo;
being well-formed.
This is basically LWG 3149:
DefaultConstructible<T>
is equivalent toConstructible<T>
(18.4.11 [concept.constructible]), which is equivalent tois_constructible_v<T>
(20.15.4.3 [meta.unary.prop]). Per 20.15.4.3 [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 typeT
can be value-initialized, rather than default-initialized as intended.
The motivation for the concept is to check if you can write:
T t;
But the definition doesn't check that, it checks if you could write T()
. But T()
also doesn't mean that you can write T{}
- there are types for which those have different meaning:
struct S0 { explicit S0() = default; }; struct S1 { S0 x; }; // Note: aggregate S1 x; // Ok S1 y{}; // ill-formed; copy-list-initializes x from {}
And the intent is to simplify, for the sake of sanity, what the library has to deal with so we want to reject S1
as just being weird.
And then once the concept is checking something different from is_default_constructible
, LWG 3338 renamed it. Since, different things should have different names.
LWG issue 3338
highlights a difference in meaning between the is_default_constructible
trait and the C++20 concept originally named default_constructible
, were LWG issue 3149 to be accepted:
3149 DefaultConstructible should require default initialization
Discussion
[...] [the concept]
DefaultConstructible<T>
requires that objects of typeT
can be value-initialized, rather than default-initialized as intended.The library needs a constraint that requires object types to be default-initializable. [...]
Users will also want a mechanism to provide such a constraint, and they're likely to choose
DefaultConstructible
despite its subtle unsuitability.
Tim Song provided an example as for when the requirement of "can be value-initialized" is too weak as compared to the stricter requirement of "can be default initialized".
Tim Song noted that
{}
is not necessarily a valid initializer for aDefaultConstructible
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 thatDefaultConstructible
should prohibit this class of pathological types by requiring that initialization form to be valid.
Issue 3149 has has since been moved to status WP (essentially accepted save for as a Technical Corrigendum).
Issue 3338 has subsequently also been given WP status, renaming the default_constructible
concept to default_initializable
:
3338. Rename default_constructible to default_initializable
Discussion
[...] It was made clear during discussion in LEWG that 3149 would change the concept to require default-initialization to be valid rather than value-initialization which the
is_default_constructible
trait requires. LEWG agreed that it would be confusing to have a trait and concept with very similar names yet slightly different meanings [...].Proposed resolution:
[...] Change the stable name "[concept.defaultconstructible]" to "[concept.default.init]" and retitle "Concept
default_constructible
" to "Conceptdefault_initializable
". Replace all references to the namedefault_constructible
withdefault_initializable
(There are 20 occurrences).
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