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

4141. Improve prohibitions on "additional storage"

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: WP Submitter: Jonathan Wakely Opened: 2024-08-22 Last modified: 2024-11-28

Priority: Not Prioritized

View all other issues in [optional.optional.general].

View all issues with WP status.

Discussion:

This issue was split out from issue 4015(i).

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 of optional<T> contains a value, it means that an object of type T, 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.

[Wrocław 2024-11-23; Status changed: Voting → WP.]

Proposed resolution:

This wording is relative to N4988.

  1. Modify 22.5.3.1 [optional.optional.general] as indicated:

    [Drafting note: This edit modifies the same paragraph as issue 4015(i), 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 of optional<T> contains a value, it means that an object of type T, referred to as the optional object's contained value, is allocated within the storage of nested 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 type optional<T> is contextually converted to bool, the conversion returns true if the object contains a value; otherwise the conversion returns false.

  2. 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 of variant holds a value of alternative type T, it means that a value of type T, referred to as the variant object's contained value, is allocated within the storage of nested within (6.7.2 [intro.object]) the variant object. Implementations are not permitted to use additional storage, such as dynamic memory, to allocate the contained value.

  3. Modify 22.8.6.1 [expected.object.general] as indicated:

    -1- Any object of type expected<T, E> either contains a value of type T or a value of type E within its own storage nested within (6.7.2 [intro.object]) it. Implementations are not permitted to use additional storage, such as dynamic memory, to allocate the object of type T or the object of type E. Member has_val indicates whether the expected<T, E> object contains an object of type T.

  4. Modify 22.8.7.1 [expected.void.general] as indicated:

    -1- Any object of type expected<T, E> either represents a value of type T, or contains a value of type E within its own storage nested within (6.7.2 [intro.object]) it. Implementations are not permitted to use additional storage, such as dynamic memory, to allocate the object of type E. Member has_val indicates whether the expected<T, E> represents a value of type T.