Skip to content
Release explainerintermediate · 2 min read · 2026-11-01

C++26 Standard Library Hardening: Precondition Bugs

C++26 Standard Library hardening helps implementations catch common library precondition mistakes that could otherwise become undefined behavior. This lesson uses vector, string, and span-style indexing as practical examples.

What changed in C++26

The ISO C++ blog entry for Bartlomiej Filipek’s article states that C++26 introduces Standard Library hardening to help catch common programming errors that previously could result in undefined behavior.

The important idea is narrower than “C++ is now fully safe.” Hardening is about making some violated Standard Library requirements easier to diagnose, instead of letting mistakes silently continue as undefined behavior.

The provided evidence does not specify one portable reporting mechanism. So do not write code that depends on hardening throwing a particular exception. Treat any hardening report as evidence that your program violated a library precondition.

The concrete bug pattern: unchecked indexing

A library precondition is a requirement the caller must satisfy before using an operation. For indexed access, the requirement is usually simple: the index must denote a valid element.

Common mistakes look like this: - v[v.size()] on a 3-element std::vector is one past the last valid element. - s[10] on the string "cpp" is not a valid character access. - view[3] on a 3-element span-style view is also one past the end.

These examples are useful because they show why hardening matters: a tiny off-by-one error can break the caller’s contract. Without a diagnostic, the bug may appear to work during testing and fail somewhere else later.

Runnable model: make the precondition explicit

The sandbox compiler for this lesson is g++ 12.2.0, so the runnable snippet below is not a real C++26 hardened Standard Library demonstration.

Instead, it models the discipline behind hardening: check the precondition before using unchecked access. The snippet throws std::out_of_range only to keep the lesson deterministic and portable in this environment.

How to write code that benefits from hardening

Do not rely on hardening as your only safety mechanism. Write code so the assumptions are visible and testable.

Use at() or explicit validation when invalid input is possible and you want controlled error handling. Use operator[] when surrounding logic has already proved the index is valid.

A common mistake is enabling diagnostics in development but ignoring the root cause because “it only happens in hardened mode.” A hardening failure should be treated as a bug in program logic, not as noise.

Who should care

This matters to C++ developers who use fast unchecked library operations in code paths involving indices, sizes, iterators, or views. These are exactly the places where off-by-one mistakes are easy to write and hard to notice.

It is also relevant for teams that use debug builds, sanitizers, CI checks, or implementation-specific hardened-library modes to catch bugs before release. The more explicit your program’s preconditions are, the easier it is for diagnostics to point at the real defect.

Code

C++ · Explicit checks for vector, string, and a span-style view✓ Executed & verified · g++ (Debian 12.2.0-14+deb12u1) 12.2.0
#include <iostream>
#include <stdexcept>
#include <string>
#include <vector>

std::out_of_range index_error(std::size_t i, std::size_t n) {
    return std::out_of_range(
        "index " + std::to_string(i) + " out of range (size " +
        std::to_string(n) + ")"
    );
}

int checked_vector_at(const std::vector<int>& v, std::size_t i) {
    if (i >= v.size()) throw index_error(i, v.size());
    return v[i];
}

char checked_string_at(const std::string& s, std::size_t i) {
    if (i >= s.size()) throw index_error(i, s.size());
    return s[i];
}

struct IntView {
    const int* data;
    std::size_t n;
};

int checked_view_at(IntView view, std::size_t i) {
    if (i >= view.n) throw index_error(i, view.n);
    return view.data[i];
}

void report(const std::string& label, void (*fn)()) {
    try {
        fn();
    } catch (const std::out_of_range& e) {
        std::cout << label << ": " << e.what() << '\n';
    }
}

int main() {
    std::vector<int> values{10, 20, 30};
    std::string word = "cpp";
    IntView view{values.data(), values.size()};

    std::cout << "safe: "
              << checked_vector_at(values, 1) << ' '
              << checked_string_at(word, 1) << ' '
              << checked_view_at(view, 2) << '\n';

    report("vector", [] { std::vector<int> v{10, 20, 30}; checked_vector_at(v, 3); });
    report("string", [] { std::string s = "cpp"; checked_string_at(s, 10); });
    report("view", [] { std::vector<int> v{10, 20, 30}; IntView view{v.data(), v.size()}; checked_view_at(view, 3); });
}

This program is not a C++26 hardening demo. It is a deterministic model of the same programming discipline: validate the index precondition before using unchecked access. The example throws std::out_of_range only because that is a clear, portable way to show the bug in this sandbox.

C++ · Conceptual precondition mistakes to avoidIllustrative · not executed
#include <span>
#include <string>
#include <vector>

void examples() {
    std::vector<int> v{10, 20, 30};
    std::string s = "cpp";
    std::span<int> sp(v);

    int a = v[v.size()]; // invalid: one past the last vector element
    char b = s[10];      // invalid: string has only 3 characters
    int c = sp[3];       // invalid: span has indices 0, 1, 2

    (void)a;
    (void)b;
    (void)c;
}

This is intentionally non-runnable. The snippet shows caller-side precondition mistakes, not a portable C++26 hardening test. Executing invalid accesses in an ordinary build would make the lesson depend on undefined behavior.

Interview questions

What is a library precondition?

A precondition is a requirement the caller must satisfy before using an operation. For indexed access, a typical precondition is that the index denotes a valid element.

Why can unchecked access be dangerous in C++?

Unchecked access assumes the caller already proved the index is valid. If that assumption is wrong, the program can have undefined behavior instead of a predictable result.

Does Standard Library hardening make C++ fully safe?

No. The provided evidence describes hardening as a way to help catch common programming errors that previously could result in undefined behavior. It is not a complete safety model for all C++ code.

Why should portable code not rely on hardening throwing an exception?

The provided evidence identifies hardening as a way to catch errors, but it does not specify a portable exception-based reporting mechanism. Use checked operations or explicit validation when your program needs controlled error handling.

When should you prefer checked access such as at() or explicit validation?

Use checked access when the index can come from untrusted, external, or complex logic and invalid values should be diagnosed or handled in a controlled way.

Quick quiz

Q1. What is the main purpose of C++26 Standard Library hardening as described in the provided evidence?
Q2. What is the exact output of the runnable snippet?
Q3. Which access is one past the end for a vector containing 3 elements?
Q4. How should you treat a hardening diagnostic?

Key takeaways

  • C++26 Standard Library hardening is intended to catch common programming errors that could otherwise become undefined behavior.
  • Unchecked indexing is safe only when caller logic has already proved the index is valid.
  • Vector, string, and span-style indexing are practical examples for understanding caller-side precondition mistakes.
  • Do not assume a hardening diagnostic is a portable, catchable exception.
  • Use at() or explicit validation when invalid indices are possible.

References

See the full C++ learning path