fn::optional<T&> and fn::just<T&> (PR #437) accept a temporary and dangle:
fn::just<int const &> j{42}; // compiles, j refers to a destroyed int
fn::optional<int const &> o{42}; // same
fn::optional<int const &> m = fn::optional<int>{1}; // same, through the converting constructor
C++26 optional<T&> deletes such constructors using std::reference_constructs_from_temporary_v, a C++23 trait: [optional.ref.ctor] deletes the constructors from U&& and from every optional<U> value category when the trait is true, and constrains the in_place_t constructor and emplace ([optional.ref.assign]) on it being false. A polyfill pfn::reference_constructs_from_temporary needs to be created, based on the compiler builtin __reference_constructs_from_temporary (or similar, depending on compiler).
The check applies to every carrier holding a reference: pfn::optional<T&>, fn::optional<T&>, fn::just<T&>, and copack reference alternatives once those are admitted (#448).
Builtin availability
__reference_constructs_from_temporary(T, U) works in C++20 mode where it exists. Checked on Compiler Explorer against nine cases with known answers (prvalue, conversion, conversion function to a value and to an lvalue, lvalue, xvalue, derived-to-base lvalue and prvalue, non-reference T):
| Compiler |
Builtin |
Matches the trait |
| GCC 12 |
no |
— |
| GCC 13 and later |
yes |
all nine cases |
| Clang 19 and later |
yes |
all nine cases |
| MSVC, Visual Studio 2022 (17.x) |
no |
— |
| MSVC, Visual Studio 18.0 to 18.2 |
no |
— |
| MSVC, Visual Studio 18.6 and later |
yes |
all nine cases (latest) |
| Apple Clang 21 |
untested |
— |
Clang's older __reference_binds_to_temporary is not a substitute: Clang 19 and 20 answer it differently from the trait for a prvalue of the referenced type (int const& from int) and for a derived prvalue (B const& from D). GCC 12 has neither builtin, and MSVC's standard library exposes std::reference_constructs_from_temporary only in versions that have the builtin.
Best effort without the builtin
Release 0.2.0 supports GCC 12 and Visual Studio 2022, so pfn falls back to a library emulation there.
The trait shapes the interface: it constrains overloads and deletes constructors, so its answer is visible through is_constructible, is_convertible, concepts and overload resolution. An error in each direction has a different cost:
- A false positive (reporting a temporary where none is created) rejects valid code on the older compilers only. That breaks the interface and is not acceptable.
- A false negative (missing a temporary) leaves a dangling construction accepted, which is the behaviour without any check.
The emulation therefore errs only towards false negatives: it reports a temporary only where reference binding rules make one certain, and reports none otherwise. Certain cases include a scalar prvalue source, a class prvalue of the referenced or a derived type, a scalar source that is not reference-compatible with the referenced type, and a non-class source converted by the referenced class type's constructor. The emulation cannot see through conversion functions, so a class source that is not reference-compatible is reported as creating no temporary. The documentation of the polyfill states this.
On compilers with the builtin, a test asserts over a matrix of cases that whenever the emulation reports a temporary, the builtin does too; the same matrix records the cases the emulation misses.
Assisted-by: Claude:claude-opus-5-5
fn::optional<T&>andfn::just<T&>(PR #437) accept a temporary and dangle:C++26
optional<T&>deletes such constructors usingstd::reference_constructs_from_temporary_v, a C++23 trait: [optional.ref.ctor] deletes the constructors fromU&&and from everyoptional<U>value category when the trait is true, and constrains thein_place_tconstructor andemplace([optional.ref.assign]) on it being false. A polyfillpfn::reference_constructs_from_temporaryneeds to be created, based on the compiler builtin__reference_constructs_from_temporary(or similar, depending on compiler).The check applies to every carrier holding a reference:
pfn::optional<T&>,fn::optional<T&>,fn::just<T&>, andcopackreference alternatives once those are admitted (#448).Builtin availability
__reference_constructs_from_temporary(T, U)works in C++20 mode where it exists. Checked on Compiler Explorer against nine cases with known answers (prvalue, conversion, conversion function to a value and to an lvalue, lvalue, xvalue, derived-to-base lvalue and prvalue, non-referenceT):Clang's older
__reference_binds_to_temporaryis not a substitute: Clang 19 and 20 answer it differently from the trait for a prvalue of the referenced type (int const&fromint) and for a derived prvalue (B const&fromD). GCC 12 has neither builtin, and MSVC's standard library exposesstd::reference_constructs_from_temporaryonly in versions that have the builtin.Best effort without the builtin
Release 0.2.0 supports GCC 12 and Visual Studio 2022, so
pfnfalls back to a library emulation there.The trait shapes the interface: it constrains overloads and deletes constructors, so its answer is visible through
is_constructible,is_convertible, concepts and overload resolution. An error in each direction has a different cost:The emulation therefore errs only towards false negatives: it reports a temporary only where reference binding rules make one certain, and reports none otherwise. Certain cases include a scalar prvalue source, a class prvalue of the referenced or a derived type, a scalar source that is not reference-compatible with the referenced type, and a non-class source converted by the referenced class type's constructor. The emulation cannot see through conversion functions, so a class source that is not reference-compatible is reported as creating no temporary. The documentation of the polyfill states this.
On compilers with the builtin, a test asserts over a matrix of cases that whenever the emulation reports a temporary, the builtin does too; the same matrix records the cases the emulation misses.
Assisted-by: Claude:claude-opus-5-5