C++26: Standard Library Hardening Experiments
C++26: Standard Library Hardening Experiments
Stay current with the evolution of Modern C++ Last Updated: August 30, 2026
Hardening has become a buzzword within the C++ community throughout 2026. This article examines the technical meaning of this term and provides concrete examples of its application.
The Fundamental Concept
To understand hardening, consider how you access elements in a std::vector. Traditionally, there are two primary methods:
std::vector<int> v { 1, 2, 3, 4 };
// Method A: Unchecked access
v[i] = 10; // Potential Undefined Behaviour (UB) if i is out of bounds
// Method B: Checked access
v.at(j) = 11; // Throws std::out_of_range if j is out of bounds
The Key Difference: While
operator[]is designed for speed and assumes the index is valid (leading to UB if it isn't),.at()provides a safety net by throwing a well-defined exception.
What Changes in C++26?
C++26 introduces the formal concept of a hardened implementation. Whether a specific library implementation is "hardened" and how a developer enables this mode is left to the implementation (the compiler/library vendor).
The Logic Shift for std::vector<T>::operator[]
The behavior of operator[](size_type pos) has evolved:
- Pre-C++26: If , the result is
Undefined Behaviour. - Since C++26: If :
- If Hardened: A contract violation occurs.
- If Not Hardened: The behavior remains undefined.
Essentially, enabling hardening replaces silent, dangerous memory corruption with a predictable, specified error.
Visualizing the Decision Process
Hardening vs. .at()
A common question is: If .at() already checks bounds, why do we need hardening for operator[]?
- Interface Consistency:
at()and[]have different signatures and performance profiles. - Error Handling: Exceptions are not always desirable. While you can disable exceptions, the resulting
std::terminateis blunt. - The Goal: Hardening doesn't turn
operator[]into.at(). Instead, it detects a logic error and terminates the program immediately, preventing the execution of memory-unsafe UB.
Integration with C++26 Contracts
Hardening is built upon the new Contracts model. In a hardened implementation, violating a hardened precondition triggers a contract violation with terminating semantics.
- Standard Contracts: Can use
ignore,observe,enforce, orquick-enforce. - Hardened Library Preconditions: Must use terminating semantics. This ensures the program stops immediately rather than allowing the developer to "observe" the error and accidentally slide back into UB.
Note: Vendors may implement these checks using internal mechanisms rather than the explicit pre, post, or contract_assert syntax.
Practical Implementation & Runtime
Does this happen at runtime?
Yes. While these checks can function within constant expressions (constexpr), their primary utility is during runtime execution.
Vendor-Specific Landscapes
C++26 aims to standardize the fragmented landscape of vendor-specific checkers:
| Vendor | Mechanism | Details |
|---|---|---|
| GCC / libstdc++ | _GLIBCXX_ASSERTIONS | Lightweight precondition checks. |
| GCC (Global) | -fhardened | Enables assertions plus broader security flags. |
| Clang / libc++ | _LIBCPP_HARDENING_MODE | Modes: NONE, FAST, EXTENSIVE, DEBUG. |
| MSVC | Specific Macros | e.g., _MSVC_STL_HARDENING_VECTOR. |
The Technical Foundation (Proposals)
As of August 2026, the feature is being finalized via these core documents:
- P3471: The primary framework for Standard Library hardening.
- P3697: Minor additions (e.g.,
basic_stacktrace,shared_ptr<T[]>,view_interface, and various iterators). - P3878: Mandates the use of terminating semantics to prevent continuing into UB after a violation.
Criteria for a "Hardened Precondition"
Not every check is a candidate for hardening. A condition is hardened only if:
- Violating it causes a memory safety issue (e.g., uninitialized memory or out-of-bounds access).
- The call site possesses all data required to perform the check.
- The check is (constant time) and has minimal overhead.
Summary of Hardened Operations in C++26
The following table outlines the types and operations now subject to hardening:
| Category | Types | Hardened Operations |
|---|---|---|
| Sequence Containers | array, vector, inplace_vector, deque, list, forward_list | operator[], front(), back(), pop_front(), pop_back() |
| Container Views | span, mdspan, view_interface | Construction, operator[], front(), back(), first(), last(), subspan() |
| Iterator Adaptors | common_iterator, counted_iterator | Construction, operator*, operator-, operator[], operator++, arithmetic, comparisons, iter_move, iter_swap |
| Strings | basic_string, basic_string_view | operator[], front(), back(), pop_back(), remove_prefix(), remove_suffix() |
| General Utilities | bitset, optional, expected | operator[], operator*, operator-, error() |
| Stacktrace | basic_stacktrace | current(), operator[] |
| Smart Pointers | shared_ptr<T[]> | operator[] |
| Numeric Arrays | valarray | operator[] |