Back in December 2025, Jason Turner published a tool on isocpp.org called the C++ Standard Evolution Viewer. The premise is simple: pick a section of the C++ standard, and see how its text changed from one version to the next. Side-by-side diffs, interactive, covering the major library and language sections across C++11 through C++26. Looking back at it now, it solves a problem that has been around for decades and somehow never had a satisfying answer.
The standard is a snapshot. When you read [class.copy] in the C++20 draft, you see what the committee decided in 2020. But C++ programmers often need to know not just the current state, but the history: when was this behavior specified, when was this edge case addressed, when did the wording tighten up? Before this tool, the answer was manual archaeology through GitHub tags and LaTeX source.
The Stable Label System Is What Makes This Possible
The C++ standard document uses two parallel systems for identifying sections: numeric headings and stable symbolic labels. The numeric headings shift constantly. What was ”§ 8.5” (initialization) in C++11 became ”§ 11.6” in C++17, because the committee inserted entire new chapters between versions. When C++20 added Concepts, the clause numbering downstream moved again. You cannot build a cross-version diff on section numbers.
Stable labels solve this. Every section, subsection, and named paragraph in the standard has a label in brackets: [dcl.init], [class.copy], [range.adaptors], [expr.prim.lambda]. These labels are intended to remain stable across versions even when everything else around them reorganizes. The draft standard source on GitHub uses them as the primary identifier throughout the LaTeX source.
That said, even labels are not perfectly stable. Between C++17 and C++20, [class.copy] was split into [class.copy.ctor] and [class.copy.assign] as the treatment of copy and move operations became complex enough to merit separate sections. Labels get renamed for clarity, get merged when related sections consolidate, or get added when new subsections are introduced. Building a reliable diff across versions requires either a hand-maintained alias table or heuristics for label matching, and either approach involves non-trivial judgment calls.
Turner’s tool focuses on what he calls Tier 1 sections: major library components and language features that provide the most educational value. This is a reasonable scope decision. The standard is roughly 1,900 pages of dense normative text; trying to make every paragraph of every annex diffable would produce noise that drowns out signal.
What You Actually See When You Diff [class.copy]
The evolution of copy and move semantics is one of the most instructive traces you can follow through the standard. In C++11, the section was new territory. Move constructors and move assignment operators were being specified for the first time, and the rules for implicitly generated special members were being laid out with enough precision to handle the interactions between user-declared destructors, user-declared copy operations, and the new move operations. The text was careful and, in places, clearly working through problems as it went.
C++14 made targeted fixes. Several corner cases in the implicit generation rules were clarified, and wording around = default was tightened. By C++17, guaranteed copy elision arrived as a significant semantic change: the standard no longer merely permitted compilers to elide copies in certain contexts (the old NRVO permission), it mandated that specific expressions would not create temporary objects at all. The [class.copy] wording had to be rewritten in places to account for this, because the old text described a world where the copy might or might not happen.
C++20 restructured the section, introduced the [class.copy.ctor] / [class.copy.assign] split, and refined the rules for conditionally-trivial special member functions, a feature that std::optional and similar vocabulary types depended on. Each of these changes has a trail in the wording, and seeing them layered on each other makes the rationale visible in a way that reading only the current draft does not.
The Ranges Adaptor Explosion in C++23
If [class.copy] shows incremental refinement, [range.adaptors] shows explosive growth. The Ranges library arrived in C++20 with a solid foundation: views like filter, transform, take, drop, join, split, and a few others. The section was large but bounded.
C++23 nearly doubled it. The committee accepted zip, zip_transform, adjacent, pairwise, chunk, slide, stride, cartesian_product, as_rvalue, enumerate, chunk_by, repeat, and as_const. Each of these required new subsections with full specification of the view type, the sentinel, the iterator category, the complexity guarantees, and the interaction with range concepts. A diff between the C++20 and C++23 text of [range.adaptors] is not a clean set of additions; it shows the committee also going back and refining existing adaptor specifications as they discovered edge cases while specifying the new ones.
This pattern, where adding new features forces revisitation of existing features, is genuinely hard to see when you only have the current draft. The Evolution Viewer makes it legible.
Context in the Tooling Ecosystem
The tool sits alongside an existing ecosystem of standard-reading infrastructure. eel.is/c++draft renders the latest working draft as hyperlinked HTML, with every stable label as an anchor and every cross-reference as a clickable link. It is the best way to read the current draft. wg21.link provides short redirects to both papers and standard sections. cppreference.com annotates individual features with version markers, the “since C++17” badges familiar to anyone who has used the site.
None of those tools show you the evolution of the normative text itself. cppreference’s annotations tell you which version introduced a feature, but not how the specification changed within a feature’s lifespan. eel.is only shows the current draft. The GitHub history of cplusplus/draft is technically the source of truth, but reading LaTeX diffs on GitHub is a punishing experience.
Turner fills that gap. The tool sits downstream of the LaTeX source, parses the custom macro set the standard uses (the \pnum paragraph markers, the \begin{note} environments, the grammar productions), and renders human-readable diffs. That parsing step is harder than it sounds. The standard LaTeX is not generic LaTeX; it uses a collection of custom macros that a naive parser will mishandle. Notes and examples are non-normative, and a good diff treats changes to them differently than changes to the normative paragraphs they accompany.
Why This Matters in Practice
The obvious use case is historical curiosity: watching the standard evolve. The more practical use case is debugging. When a piece of code behaves differently with -std=c++17 than with -std=c++14 and you cannot immediately tell why, the question is what changed in the relevant section. Right now, the path to that answer involves knowing which section to look at, cloning the draft repo, checking out two tags, and running a diff on the LaTeX. Most developers do not do that; they ask on Stack Overflow or look at compiler changelogs.
Having a clean, interactive view of section history lowers the cost of going to the primary source. It also supports teaching. If you are explaining to a colleague why aggregate initialization works the way it does in C++20 and why that was a mild breaking change from C++17, showing them the actual wording change is more grounding than any paraphrase.
Jason Turner has spent years building infrastructure for taking the C++ standard seriously as a primary source: C++ Weekly, C++ Best Practices, project templates that wire in static analysis and sanitizers from the start. The Evolution Viewer is that same instinct applied to the document itself, treating it as something worth reading carefully over time rather than consulting only when all other options are exhausted.
The C++ standard is 35 years old and still changing substantially with every release. Tools that make that change legible are not a luxury.