Hardened preconditions for indirect and polymorphic

Document #: P4349R0 [Latest] [Status]
Date: 2026-08-21
Project: Programming Language C++
Audience: Library Evolution Working Group
Reply-to: Tristan Brindle
<>
Jonathan Coe
<>
Antony Peacock
<>

1 Introduction

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.

2 Proposed wording

Changes are relative to [N5054].

2.1 indirect

Modify section 20.5.1.6 Observers [indirect.obs] as indicated:

constexpr const T& operator*() const & noexcept;

constexpr T& operator*() & noexcept;

1 Hardened Ppreconditions: *this is not valuelessvalueless_after_move() is false.

2 Returns: *p.

constexpr const T&& operator*() const && noexcept;

constexpr T&& operator*() && noexcept;

3 Hardened Ppreconditions: *this is not valuelessvalueless_after_move() is false.

4 Returns: std​::​move(*p).

constexpr const_pointer operator->() const noexcept;

constexpr pointer operator->() noexcept;

5 Hardened Ppreconditions: *this is not valuelessvalueless_after_move() is false.

6 Returns: p.

2.2 polymorphic

Modify section 20.5.2.6 Observers [polymorphic.obs] as indicated:

constexpr const T& operator*() const noexcept;

constexpr T& operator*() noexcept;

1 Hardened Ppreconditions: *this is not valuelessvalueless_after_move() is false.

2 Returns: A reference to the owned object.

constexpr const_pointer operator->() const noexcept;

constexpr pointer operator->() noexcept;

3 Hardened Ppreconditions: *this is not valuelessvalueless_after_move() is false.

4 Returns: A pointer to the owned object.

3 References

[N5054] Thomas Köppe. 2026-07-16. Working Draft, Programming Languages — C++.
https://wg21.link/n5054
[P3019R14] Jonathan Coe, Antony Peacock, Sean Parent. 2025-02-11. Vocabulary Types for Composite Class Design.
https://wg21.link/p3019r14
[P3471R4] Konstantin Varlamov, Louis Dionne. 2025-02-14. Standard Library Hardening.
https://wg21.link/p3471r4