indirect and
polymorphic| Document #: | P4349R0 [Latest] [Status] |
| Date: | 2026-08-21 |
| Project: | Programming Language C++ |
| Audience: |
Library Evolution Working Group |
| Reply-to: |
Tristan Brindle <tcbrindle@gmail.com> Jonathan Coe <jonathanbcoe@gmail.com> Antony Peacock <ant.peacock@gmail.com> |
C++26 introduced two new “vocabulary types for composite class
design”,
std::indirect
and
std::polymorphic,
via [P3019R14].
Internally, these types hold a pointer to a heap-allocated value;
when moving, ownership of the heap allocation is transferred and the
source object is left in a valueless state. Attempting to
access the contained object of a valueless
indirect or
polymorphic instance via
operator*
or
operator->
is undefined behaviour. It is up to the programmer to avoid this
situation by first ensuring that
valueless_after_move()
is false
before dereferencing.
At the same time, C++26 introduced hardened preconditions
for certain standard library facilities in [P3471R4]. In particular,
that paper added hardened preconditions to
operator*
and
operator->
of
std::optional
and
std::expected,
to help avoid undefined behaviour by requiring that
has_value()
is true.
Exactly the same motivation for hardening the dereference operators
applies to indirect and
polymorphic as it does for
optional and
expected, and the check is similarly
cheap to make (just a
nullptr
comparison). It is rather unforunate that these new additions to C++26
did not benefit from the same safety improvements made to existing
facilities, due to the overlapping timing of the proposals.
This paper therefore proposes that we add hardened preconditions to
the dereference operators of
indirect and
polymorphic.
Changes are relative to [N5054].
indirectModify section 20.5.1.6 Observers [indirect.obs] as indicated:
constexpr const T& operator*() const & noexcept;
constexpr T& operator*() & noexcept;1 Hardened
Ppreconditions:*thisis not valuelessvalueless_after_move()isfalse.2 Returns:
*p.
constexpr const T&& operator*() const && noexcept;
constexpr T&& operator*() && noexcept;3 Hardened
Ppreconditions:*thisis not valuelessvalueless_after_move()isfalse.4 Returns:
std::move(*p).
constexpr const_pointer operator->() const noexcept;
constexpr pointer operator->() noexcept;5 Hardened
Ppreconditions:*thisis not valuelessvalueless_after_move()isfalse.6 Returns:
p.
polymorphicModify section 20.5.2.6 Observers [polymorphic.obs] as indicated:
constexpr const T& operator*() const noexcept;
constexpr T& operator*() noexcept;1 Hardened
Ppreconditions:*thisis not valuelessvalueless_after_move()isfalse.2 Returns: A reference to the owned object.
constexpr const_pointer operator->() const noexcept;
constexpr pointer operator->() noexcept;3 Hardened
Ppreconditions:*thisis not valuelessvalueless_after_move()isfalse.4 Returns: A pointer to the owned object.