Section: 22.5.3.1 [optional.optional.general], 22.6.3.1 [variant.variant.general], 22.8.6.1 [expected.object.general], 22.8.7.1 [expected.void.general] Status: Tentatively Ready Submitter: Jonathan Wakely Opened: 2024-08-22 Last modified: 2024-09-18
Priority: Not Prioritized
View other active issues in [optional.optional.general].
View all other issues in [optional.optional.general].
View all issues with Tentatively Ready status.
Discussion:
This issue was split out from issue 4015.
optional
, variant
and expected
all use similar wording to require
their contained value to be a subobject, rather than dynamically allocated
and referred to by a pointer, e.g.
When an instance ofoptional<T>
contains a value, it means that an object of typeT
, referred to as the optional object’s contained value, is allocated within the storage of the optional object. Implementations are not permitted to use additional storage, such as dynamic memory, to allocate its contained value.
During the LWG reviews of P2300 in St. Louis, concerns were raised about the form of this wording and whether it's normatively meaningful. Except for the special case of standard-layout class types, the standard has very few requirements on where or how storage for subobjects is allocated. The library should not be trying to dictate more than the language guarantees. It would be better to refer to wording from 6.7.2 [intro.object] such as subobject, provides storage, or nested within. Any of these terms would provide the desired properties, without using different (and possibly inconsistent) terminology.
Using an array of bytes to provide storage for the contained value would
make it tricky to meet the constexpr requirements of types like optional
.
This means in practice, the most restrictive of these terms, subobject,
is probably accurate and the only plausible implementation strategy.
However, I don't see any reason to outlaw other implementation strategies that
might be possible in future (say, with a constexpr type cast, or non-standard
compiler-specific instrinics).
For this reason, the proposed resolution below uses nested within,
which provides the desired guarantee without imposing additional restrictions
on implementations.
[2024-09-18; Reflector poll]
Set status to Tentatively Ready after seven votes in favour during reflector poll.
Proposed resolution:
This wording is relative to N4988.
Modify 22.5.3.1 [optional.optional.general] as indicated:
[Drafting note: This edit modifies the same paragraph as issue 4015, but that other issue intentionally doesn't touch the affected sentence here (except for removing the italics on "contained value"). The intention is that the merge conflict can be resolved in the obvious way: "An optional object's contained value is nested within (6.7.2 [intro.object]) the optional object."]
-1- Any instance of
optional<T>
at any given time either contains a value or does not contain a value. When an instance ofoptional<T>
contains a value, it means that an object of typeT
, referred to as the optional object's contained value, isallocated within the storage ofnested within (6.7.2 [intro.object]) the optional object.Implementations are not permitted to use additional storage, such as dynamic memory, to allocate its contained value.When an object of typeoptional<T>
is contextually converted tobool
, the conversion returnstrue
if the object contains a value; otherwise the conversion returnsfalse
.
Modify 22.6.3.1 [variant.variant.general] as indicated:
-1- Any instance of
variant
at any given time either holds a value of one of its alternative types or holds no value. When an instance ofvariant
holds a value of alternative typeT
, it means that a value of typeT
, referred to as thevariant
object's contained value, isallocated within the storage ofnested within (6.7.2 [intro.object]) thevariant
object.Implementations are not permitted to use additional storage, such as dynamic memory, to allocate the contained value.
Modify 22.8.6.1 [expected.object.general] as indicated:
-1- Any object of type
expected<T, E>
either contains a value of typeT
or a value of typeE
within its own storagenested within (6.7.2 [intro.object]) it.Implementations are not permitted to use additional storage, such as dynamic memory, to allocate the object of typeMemberT
or the object of typeE
.has_val
indicates whether theexpected<T, E>
object contains an object of typeT
.
Modify 22.8.7.1 [expected.void.general] as indicated:
-1- Any object of type
expected<T, E>
either represents a value of typeT
, or contains a value of typeE
within its own storagenested within (6.7.2 [intro.object]) it.Implementations are not permitted to use additional storage, such as dynamic memory, to allocate the object of typeMemberE
.has_val
indicates whether theexpected<T, E>
represents a value of typeT
.