The C++ Standard Library Has Been Walking Itself Back for Fifteen Years, and the Receipts Are Public - HFT University
LIVE SUBMISSIONS — 3 DAYS
The C++ Standard Library Has Been Walking Itself Back for Fifteen Years, and the Receipts Are Public
Published: May 23, 2026
The C++ standard library has been walking itself back for fifteen years, and the receipts are public
Sandor Dargo's post this month on std::copyable_function closes with a quick-reference table. Four callable wrappers, one recommendation each, and at the bottom of the list one entry that should stop any working C++ engineer cold:
std::function: Legacy. Avoid in new code.
std::function shipped in C++11. The committee spent fifteen years shipping the wrappers that should replace it. The latest, std::copyable_function, lands in C++26. The recommendation written on top of the new arrival is not "use this when you need a copyable callable." It is "do not use the original."
This is not unusual. The C++ committee has been writing that sentence about its own features since C++11 was new. Sometimes the sentence is formal (a paper number, a deprecation in the standard, a removal one cycle later). Sometimes the sentence is what every senior engineer tells every junior engineer on day one ("never reach for that, here is what to use instead"). And sometimes the sentence cannot be written into the standard at all, because the broken thing is locked in by ABI compatibility, so it stays in the standard library as the default that every tutorial reaches for and every production codebase quietly replaces. The pattern is so consistent that it deserves its own catalogue, with paper numbers next to every entry, so the next time someone tells you the new C++ feature is the future you can ask them to estimate how long until the next paper deprecates it.
This piece is that catalogue, in three tiers. The first tier is the formal walk-backs the committee has written down. The second tier is the "everyone knows to avoid this" walk-backs that the committee has not formalised. The third tier is the most damning, because it is the standard library containers that almost every C++ codebase uses every day and that the committee cannot fix without breaking ABI. We have receipts on the third tier from our own Rust-vs-C++ multi-book benchmark, which measured 58 times the P99 latency between Rust's standard library and C++'s on identical workloads with identical isolation, and which traced the gap to three containers the committee has never formally said are broken.
Tier 1: The formal walk-backs the committee has written down
Every entry below points at a real paper that the working group adopted. None of these are arguments. They are admissions in writing.
The cleanest historical case is std::auto_ptr , the C++98 smart pointer whose copy-as-move semantics broke generic code and standard containers from the day it shipped. Deprecated in C++11, removed in C++17 by N4190 "Removing auto_ptr, random_shuffle(), And Old Stuff" , Stephan T. Lavavej. That single paper also took out the entire adapter zoo from C++98: std::bind1st, std::bind2nd, std::ptr_fun, std::mem_fun, std::mem_fun_ref, std::unary_function, std::binary_function, std::pointer_to_unary_function, std::pointer_to_binary_function. The replacement is the lambda, a language feature that landed two cycles earlier and made the entire adapter framework irrelevant. std::random_shuffle went with them, deprecated in C++14, removed in C++17, replaced by std::shuffle because the original depended on std::rand and global state.
Dynamic exception specifications (throw(X, Y)) were the C++98 mechanism for declaring which exceptions a function could throw. Deprecated in C++11, removed in C++17 by P0003R5 , Alisdair Meredith. Replacement: noexcept. The vestigial throw() synonym for noexcept(true) survived until C++20, when P1152 finally killed it. Eighteen years of standard text spent un-shipping an exception model.
std::iterator , the C++98 base class every "Effective C++" book taught you to inherit from, was deprecated in C++17 by P0174R2 ("Deprecating Vestigial Library Parts in C++17", Meredith). Removal is now proposed for C++26 in P3365R1 . The replacement is "define the five typedefs yourself", which is what most engineers were doing anyway because inheriting from std::iterator never gave you anything useful.
std::aligned_storage and std::aligned_union shipped in C++11, were deprecated in C++23 by P1413R3 (CJ Johnson, Google). The paper's rationale is worth quoting because it captures the committee admitting a design error on its own work. The deprecated types require typename ::type boilerplate, require reinterpret_cast to access the contents, treat Len == 0 as undefined behaviour, and are not constexpr. The replacement is "use alignas(T) std::byte[sizeof(T)] directly", which is what the standard probably should have shipped in the first place.
std::not1/std::not2 and the unary_negate/binary_negate adapters were...