C++26: Standard library hardening | Sandor Dargo's Blog<br>Sandor Dargo's Blog<br>On C++, software development and books
HOME TAGS ARCHIVES BOOKS SPEAKING DAILY C++ WORKSHOPS HI... SUBSCRIBE
Blog 2026 05 13 C++26: Standard library hardening Post<br>Cancel
C++26: Standard library hardening<br>Sandor Dargo May 13 2026-05-13T00:00:00+02:00<br>4 min
Undefined behavior (UB) in C++ is one of the hardest categories of bugs to deal with. It can silently corrupt memory, cause crashes far from the actual mistake, or — worst of all — just happens to work on your machine. A significant share of UB in real codebases comes not from exotic language features, but from basic misuse of the standard library: accessing a vector out of bounds, calling front() on an empty container, or dereferencing an empty optional.<br>C++26 addresses this directly with standard library hardening, introduced via P3471R4.<br>What is library hardening?<br>Library hardening converts certain undefined behavior in the standard library into detectable contract violations at runtime. When a hardened precondition is violated, the runtime reacts before any other observable side effect — think of it as adding bounds checking to operations that are UB today.<br>This is not a new idea. All three major standard library implementations already ship vendor-specific hardening modes. The problem is that these mechanisms are all different, non-portable, and inconsistently specified. P3471R4 standardizes what implementations already do.<br>The standardized version of hardening becomes the first use of contracts (specified in P2900). This aligns perfectly with the direction of the language.<br>The motivation: real-world evidence<br>The strongest argument for this feature is Google’s production experience, which the proposal references: applying hardened libc++ across “hundreds of millions of lines of C++” code found over 1,000 bugs, including security-critical ones. The average performance overhead was a surprisingly low 0.30% — one third of a percent. That overhead came down thanks to the compiler’s ability to eliminate redundant checks during optimization.<br>The impact went beyond security: teams observed a 30% reduction in baseline segmentation fault rates in production, pointing to improved code correctness across the board.<br>What a remarkable result!<br>What gets hardened?<br>The proposal focuses deliberately on memory safety preconditions only. Checking all preconditions “is an explicit non-goal” — the goal is to catch the checks that are cheap to add and high-value to have.<br>std::span<br>operator[]: requires idx front(), back(): require non-emptyfirst(), last(), subspan(): validate that count and offset are in boundsConstructors from a range: verifies the extent matchesstd::string_view<br>operator[]: requires pos front(), back(): require non-emptyremove_prefix(), remove_suffix(): require n Sequence containers (vector, deque, list, forward_list, array, string)<br>operator[]: requires n (for basic_string, n since accessing the null terminator is valid)front(), back(): require !empty()pop_front(), pop_back(): require !empty()std::optional and std::expected<br>operator->(), operator*() on optional: require has_value()Value access on expected: requires has_value()error() on expected: requires !has_value()std::mdspan<br>operator[]: validates all multidimensional indices within their extentsConstructor: verifies static extents match during conversionsstd::bitset and std::valarray<br>operator[]: requires pos / n Some things are intentionally left out. Iterator-based operations like erase(), associative containers, and algorithms are deferred — they require more complex validity checks that don’t fit the scope of this proposal.<br>Concrete examples<br>Here are a couple of examples of what library hardening catches that today silently causes UB:<br>std::vectorint> v = {1, 2, 3};<br>int x = v[5]; // contract violation: 5 >= 3<br>v.pop_back();<br>v.pop_back();<br>v.pop_back();<br>v.pop_back(); // contract violation: !empty() is false
std::string_view sv("hello");<br>char c = sv[10]; // contract violation: 10 >= 5<br>sv.remove_prefix(10); // contract violation: 10 > 5
std::optionalint> opt;<br>int x = *opt; // contract violation: has_value() is false
std::spanint, 5> sp(data, 3); // contract violation: extent mismatch<br>sp.first10>(); // contract violation: 10 > size()
None of these are difficult-to-imagine-to-happen situations. These are the kinds of bugs that slip through in code reviews and only surface in production.<br>How con you activate it?<br>The proposal does not standardize the activation mechanism — this is intentionally left to implementations. In practice, you will see something like:<br>A compiler flag: -fhardened or -D_LIBCPP_HARDENING_MODE=...Or a build-system option specific to the implementationOne deliberate design choice: in a hardened implementation, you cannot override a hardened precondition check with ignore semantics. The whole point of hardening is to provide a firm safety baseline. If you could turn off individual checks arbitrarily, the...