The Ecosystem Gap: What the C++ Standard Cannot Specify
Great Founder Theory in C++
Abstract
The C++ standard specifies the language but not its ecosystem: ABI, build systems, package management. The committee’s de facto policy of ABI stability—formalized in Prague 2020—freezes the standard library’s mistakes permanently. This paper argues that structural limitations in C++ governance prevent the ecosystem coordination that Python and Rust achieved, and that the gap is widening.
1. Introduction
A colleague once explained why the Networking TS couldn’t be standardized: it doesn’t support coroutines. I countered that Asio—its reference implementation—proves the design can adapt. His response stopped me cold: “Adaptation requires changes. Template parameters, function signatures, struct layouts. Once it’s in the standard, any change is an ABI break.”
The implication: every feature must be perfect when standardized. No iteration. No lessons learned. Perfect or frozen forever.
This is the ecosystem gap. The C++ standard defines syntax, semantics, and library interfaces. It does not define how compiled modules interoperate, how developers obtain dependencies, or how tools analyze code. These concerns are left to implementations and convention—which means they fragment. A library compiled with GCC cannot link against MSVC. Package managers are incompatible. Every dependency is a risk.
Python made different choices. Breaking backwards compatibility enabled PyPI’s 700,000 packages. The cost was a decade of pain. The benefit was a coherent ecosystem that C++ developers can only envy.
This paper examines what the standard cannot specify, why the committee chose ABI stability, and what consequences follow.
2. The C++ Ecosystem: What the Standard Does Not Define
2.1 Application Binary Interface (ABI)
The ABI encompasses all conventions necessary for separately compiled modules to interoperate: name mangling, calling conventions, object layout, vtable structure, RTTI format, and exception handling mechanisms. None of these are standardized by WG21.
Name mangling illustrates the fragmentation. GCC and Clang follow the Itanium C++ ABI, producing symbols like _Z3fooi for void foo(int). MSVC uses a different scheme, producing ?foo@@YAXH@Z. These are incompatible. Code compiled with GCC cannot be linked against code compiled with MSVC, even when both compilers target the same platform and implement the same C++ standard version.
Object layout presents similar challenges. The standard permits implementations to choose padding, alignment, and virtual base placement. The result is that sizeof(T) may differ across compilers. A structure with particular member ordering may have different layouts. Virtual inheritance, multiple inheritance, and empty base optimization all have implementation-defined behaviors that affect ABI.
Exception handling mechanisms diverge sharply. The Itanium ABI uses DWARF-based unwinding; MSVC uses Structured Exception Handling (SEH) on Windows. These mechanisms are architecturally incompatible—not merely different conventions, but fundamentally different approaches to stack unwinding and cleanup.
2.2 Standard Library Implementations
Three major standard library implementations exist: libstdc++ (GCC), libc++ (LLVM), and the MSVC STL. They are ABI-incompatible with each other—and sometimes with themselves across versions.
The most notorious example: std::string changed layout in C++11. A library built in 2010 cannot link with code built in 2015, even using the same compiler. Some distributions still default to the pre-C++11 ABI.
2.3 Build Systems and Package Management
The C++ standard specifies no build system. CMake has emerged as the de facto standard through market dominance rather than standardization. Even so, CMake usage is far from universal, and CMake itself is merely a meta-build system that generates native build files.
Package management is worse. No standard package manager exists. vcpkg, Conan, and Spack each have significant adoption and incompatible approaches. Many C++ developers manage dependencies manually, copying source files into their projects. The phrase “header-only library” is a marketing advantage because it sidesteps the linking problems that plague the ecosystem.
The cultural consequences are significant. C++ developers are famously reluctant to introduce dependencies. Libraries advertise “no external dependencies” as a virtue. This reluctance is rational given the ecosystem’s fragmentation—each dependency introduces potential ABI incompatibility, build system complexity, and platform-specific failures. The result is that C++ developers frequently reimplement functionality that would be a one-line import in other ecosystems.
3. The Python Contrast: A Different Philosophy
3.1 Unified Ecosystem by Design
Python has PyPI (700,000+ packages), pip, and a governance structure that can make binding decisions about ecosystem infrastructure. Installing a dependency is one command. This unity did not happen by accident—it required centralized authority willing to impose standards.
3.2 The Python 2 to Python 3 Transition
Python 3.0 broke backwards compatibility. The transition took over a decade, fragmented the ecosystem, and cost the industry enormously. Yet it completed. Python 2.7 reached end-of-life in January 2020. The painful migration is over. The language emerged unified, able to move forward without perpetually supporting legacy decisions.
3.3 Why Python Could Break Compatibility
Python’s ability to execute a breaking change stemmed from several factors that C++ lacks. The analogy is imperfect—Python 2 to 3 was an API and semantic break, not just an ABI break, and Python was at an earlier stage with fewer deep dependencies. Still, the comparison illuminates what makes such transitions possible.
First, Python is an interpreted language. Code is distributed as source or bytecode, not compiled binaries. The interpreter handles compatibility. There are no symbol mangling incompatibilities, no layout differences, no calling convention mismatches. This made the Python 3 transition painful but tractable—without this property, it might have been impossible.
Second, Python had centralized governance. Guido van Rossum made the decision to break compatibility, and the decision stood. The BDFL model concentrated authority in a single individual with the legitimacy and technical vision to make difficult tradeoffs. When Guido said Python 2 would eventually end, the ecosystem adapted—not immediately, not willingly, but eventually. C++’s consensus-based governance cannot produce such decisive action; it is structurally tuned to avoid disruption, which also means it cannot execute disruptive solutions even when they would be beneficial.
Third, Python’s value proposition differs from C++’s. Python optimizes for developer productivity, not execution performance. The cost of an interpreter restart or a recompiled dependency is measured in seconds, not hours. Breaking changes impose costs, but those costs are bounded by the speed of a pip install.
4. The C++ ABI Stability Decision
4.1 The Prague Vote
In February 2020, at the Prague meeting, WG21 took a series of polls on whether to break ABI for C++23. The committee voted against breaking ABI. As one observer noted, “there was no applause.”
Corentin Jabot captured the moment: “Each passing day makes the problem a bit worse and more expensive. I have no confidence that the poll, if taken again in 3 years, would be any different. It’s like climate change—everybody agrees we should invest in that problem someday.”
The decision formalized what had been implicit practice. Formally, the C++ standard says nothing about ABI—it is entirely outside the standard’s scope. Yet implementers have historically held a de facto veto on any proposal that would break ABI stability. The Prague vote made this veto explicit policy.
Paper P2028R0 (”What is ABI, and What Should WG21 Do About It?”) outlined the stakes: “If we promise ABI stability, I believe that industry involvement in (and dependence upon) the standard library will diminish. Concretely, I believe that Google’s interest in the standard library will be limited to primitives that are demonstrably efficient, are expensive to copy, and that commonly pass through interface boundaries.”
4.2 The Cost of ABI Stability
ABI stability imposes constraints that accumulate over time. Every standard library type’s layout is frozen once shipped. Every function signature is permanent. Every decision, optimal or not, becomes permanent.
Titus Winters quantified the aggregate cost in P1863R0: “All known performance concerns that are blocked solely by ABI easily add to a performance penalty of a few percentage points—perhaps 5-10%.” For the language that “claims to leave no room for a more efficient language,” this is a damning admission.
The consequences are concrete. std::regex is notoriously slow—slower, in some benchmarks, than spawning a PHP process to execute a regular expression. It cannot be fixed without breaking ABI. std::unique_ptr has overhead compared to raw pointers due to calling convention constraints. std::unordered_map‘s API and ABI force suboptimal implementation strategies compared to alternatives like Swiss Tables.
int128_t has never been standardized because modifying intmax_t would break ABI. scoped_lock was added rather than fixing lock_guard to avoid ABI breakage. Features that would require ABI changes are rejected or redesigned to work within ABI constraints, regardless of technical merit.
The cost of an ecosystem-wide ABI break is real: coordinating rebuilds across every provider of plugins, shared libraries, and DLLs requires significant effort. This explains the reluctance to break. It also suggests that the longer ABI remains stable, the more expensive any eventual break becomes. Each passing year compounds the problem.
4.3 The Stability-Evolution Tradeoff
ABI stability and the ability to evolve the standard library are fundamentally incompatible. This is not merely about performance optimization—it is about fixing design flaws, adding capabilities, and adapting to new language features.
Consider: std::variant shipped with a flaw—it can become valueless. std::filesystem::path has encoding problems that require ABI changes to fix. The Networking TS cannot be adapted to support coroutines without changing template parameters and function signatures. std::error_code cannot gain source location support or cross-DLL category comparison. None of these are performance issues. All are frozen by ABI stability.
C++ historically prioritized the ability to change—”zero-cost abstractions” assumes the freedom to discover and implement the zero-cost solution. ABI stability forecloses that freedom. The committee voted for stability without explicitly acknowledging this tradeoff against the language’s founding principles.
5. User Stories: ABI Stability in Practice
5.1 The Networking TS
The Networking TS story from the introduction illustrates a broader pattern: features must be perfect when standardized. No iteration. No lessons learned. Ship complete or not at all.
This becomes absurd for large frameworks. Sender/receiver represents years of top-down theoretical design, not bottom-up use-case refinement. Is it perfect? ABI stability demands that it be.
5.2 std::error_code
Boost.System’s error_code was standardized in C++11. Development in Boost continued.
Boost’s version now supports embedding a source location—the exact file and line where an error was assigned. Invaluable for debugging. The author wanted to propose this for the standard. Cannot advance: adding a pointer changes the type’s layout. ABI break.
Boost’s error_category now contains a 64-bit unique identifier for fast comparison. This solves a real problem: when different DLLs link the same libraries, they get separate copies of category objects. In the standard, these compare unequal because comparison uses addresses. Boost’s ID means same category, same ID, regardless of address. Cannot propose for the standard: changes error_category‘s layout. ABI break.
std::error_code is a vocabulary type—its value comes from universal adoption. Yet the standard version is now frozen while Boost’s evolves. Libraries needing these features must choose Boost, fragmenting the vocabulary the standard was meant to unify.
5.3 The Standard Library Graveyard
Some standard library components are beyond repair.
std::regex is 10-100x slower than equivalents in Rust or Go, and often 2x slower than Python. The committee’s official response (P1844R1): “We don’t want to spend resources enhancing std::regex when our present guidance is to use something else if at all possible.” The committee has given up.
std::unique_ptr is not zero-cost. According to platform ABIs, “non-trivial” types like unique_ptr are passed via the stack rather than registers, even when passed “by value.” The abstraction that promises zero overhead has measurable overhead. Clang’s libc++ attempted a fix with [[clang::trivial_abi]], but it’s non-standard and changes observable behavior.
std::unordered_map forces suboptimal implementation strategies. Google’s SwissTable containers are dramatically faster—but can never replace the standard version. The committee knew better designs existed and couldn’t adopt them.
std::vector cannot support small-buffer optimization because ABI stability requires pointer and iterator stability across moves.
These aren’t theoretical concerns. They’re permanent taxes on every C++ program that uses the standard library.
6. Institutional Analysis: Why C++ Cannot Do What Python Did
6.1 Governance Structure
WG21 is an ISO working group operating under consensus procedures. Proposals must navigate multiple study groups (SG1-SG23+), working groups (EWG, LEWG, LWG, CWG), and ultimately plenary votes. This structure ensures broad input but makes decisive action difficult.
Contrast this with Python’s BDFL model. When Guido van Rossum decided Python 3 would break compatibility with Python 2, that decision was final. There was no vote. There was no consensus-building among competing interests. The decision was made by someone with the authority, legitimacy, and technical vision to make it.
C++ has no equivalent figure. Bjarne Stroustrup created C++ and remains active in its evolution, but he participates through the same process as everyone else—writing papers, seeking consensus, navigating committee procedures. No individual has the authority to mandate an ABI break over vendor objections.
6.2 Vendor Dynamics
C++ implementation is dominated by a small number of vendors: GCC (Red Hat, GNU), Clang (Apple, Google, LLVM Foundation), and MSVC (Microsoft). These vendors have customers with vast codebases compiled against current ABIs. Breaking ABI imposes costs on those customers—costs measured in engineering time, testing effort, and deployment risk.
Vendors can effectively veto ABI-breaking changes by refusing to implement them. If Microsoft decides MSVC will maintain ABI stability, any standard requirement that breaks MSVC ABI will be ignored or worked around. The standard cannot compel vendor behavior; it can only specify what conforming implementations must do.
Python’s vendor dynamics differ dramatically. CPython is the reference implementation, maintained by the Python Software Foundation and a community of contributors. Alternative implementations (PyPy, Jython, IronPython) exist but follow CPython’s lead. There is no Microsoft equivalent insisting on backwards compatibility against the community’s direction.
6.3 Process Ossification
WG21’s process was designed for standards development, not ecosystem coordination. It excels at achieving consensus on language features among compiler vendors and library implementers. It has no mechanism for mandating build system standards, package management conventions, or ABI specifications.
The committee can recommend. It cannot compel. When SG15 (Tooling) discusses build system improvements, no path exists to make those improvements binding. When SG16 (Unicode) develops text handling facilities, it cannot mandate that all compilers support UTF-8 source files identically. The standard’s scope is structurally limited to what implementations voluntarily adopt.
This is not a bug but a feature of ISO standardization. ISO standards describe; they do not govern. The same flexibility that allows C++ to target diverse platforms—from microcontrollers to supercomputers—prevents the kind of ecosystem unification that Python achieved through centralized authority.
The standard did not cause C++’s ecosystem fragmentation—that fragmentation predates ISO standardization. But the standard is not helping either. A new C++ version every three years, each adding features with their own implementation timelines, compounds the compatibility challenges rather than resolving them.
The damage extends beyond rejected proposals. As one committee member observed in P1654R0: “I suspect that there are many ideas that experienced committee members are filtering early because we have internalized ‘that’s an ABI break and thus a non-starter.’” The committee doesn’t just reject ABI-breaking proposals—it has stopped receiving them.
7. Consequences and Implications
7.1 The Standard Library’s Diminishing Relevance
If ABI stability prevents the standard library from evolving, users who need more than it provides will abandon it. Google’s Abseil, Facebook’s Folly, Bloomberg’s BDE—these corporate libraries exist partly because the standard library cannot meet their requirements and cannot evolve to do so.
The irony is acute. The standard library exists to provide a common vocabulary for C++ programs. When users abandon it for alternatives, the vocabulary fragments. Different organizations use different string types, different containers, different error handling. The standard library becomes a lowest-common-denominator solution for projects that can tolerate its limitations.
This fragmentation reveals a deeper dysfunction. Python and Rust support “towers of abstraction”—libraries building on libraries, leveraging shared infrastructure to create increasingly sophisticated capabilities. C++ cannot. The ecosystem’s fragmentation means developers avoid dependencies, reimplement functionality locally, and “bottom out” at a much lower level of abstraction. Companies create monolithic internal libraries (Folly, BDE, Boost) precisely because the broader ecosystem is too fragmented to rely upon. Each organization reinvents what healthier ecosystems would share.
7.2 Competitive Pressure and the Path Not Taken
Rust demonstrates an alternative: aggressive evolution with edition boundaries, a unified package ecosystem (crates.io, 150,000+ crates), and governance that can make decisions. Memory safety concerns from the NSA and CISA add urgency. Bjarne Stroustrup’s March 2025 “call to defend C++” acknowledged what many perceive as an existential threat.
C++’s fragmentation is not an inevitable consequence of targeting native code. It is a consequence of choices. Python and Rust prove that unified ecosystems are achievable. A planned ABI break—announced years in advance, with tooling support and a hard cutoff—is survivable. The transition is painful but finite. The alternative—permanent accumulation of technical debt—is infinite pain, distributed across all future development.
8. Conclusion
The C++ standard cannot specify what the ecosystem needs. ABI, build systems, package management—all outside scope. Within this structure, the committee chose stability over evolution. The consequences compound: a standard library that cannot improve, vocabulary types frozen with known flaws, a widening gap between what the standard offers and what users need.
This paper identifies problems without claiming to solve them. Solutions require answers to hard questions:
How would a standard package manager be chosen from the dozen that exist?
How would a uniform build system displace entrenched alternatives?
How would closed-source platform vendors be convinced to adopt a cross-platform binary specification?
Who would be granted the authority to make such decisions?
These questions cannot be answered by one person or one paper. They require leaders across the C++ ecosystem—in companies, in compiler teams, in the committee—to recognize the problem and collaborate. Rust and Python solved these problems. Their solutions may be adaptable to C++.
The people who built C++ are still active. Bjarne Stroustrup still writes papers. Herb Sutter, who convened the committee for fifteen years, retired as convener this month but remains involved. The institutional knowledge still exists. But the window is closing. As Corentin Jabot wrote after Prague: “The simple fact that we had this conversation and voted not to break ABI tends to show that the ecosystem is ossifying—and ossifying fast.”
Every year that passes, the ecosystem gap widens, the technical debt compounds, and alternatives grow stronger. C++ will not fix itself. Someone has to act.
9. Acknowledgments
The author thanks Peter Dimov for his pragmatic assessments of standard library facilities, Titus Winters for his detailed analysis of ABI concerns in P1863R0 and P2028R0, and the numerous committee members whose papers and discussions informed this analysis.
10. References
WG21 Papers
[P1654R0] Ben Craig, “ABI breakage - summary of initial comments”, 2019. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1654r0.html
[P1844R1] Hana Dusíková, “Enhancement of std::regex”, 2019. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1844r1.html
[P1863R0] Titus Winters, “ABI - Now or Never”, 2019. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2019/p1863r0.pdf
[P2028R0] Titus Winters, “What is ABI, and What Should WG21 Do About It?”, 2020. https://www.open-std.org/jtc1/sc22/wg21/docs/papers/2020/p2028r0.pdf
Blog Posts and Analysis
Corentin Jabot, “The Day The Standard Library Died”, 2020. https://cor3ntin.github.io/posts/abi/
Pigweed Team, “Coroutines in Embedded C++”, 2024. https://pigweed.dev/blog/05-coroutines.html
libc++ Documentation, “Unique Pointer Trivial ABI”. https://libcxx.llvm.org/DesignDocs/UniquePtrTrivialAbi.html
Specifications and Governance
[Itanium C++ ABI] https://itanium-cxx-abi.github.io/cxx-abi/
[PEP 13] Python Language Governance
[PEP 387] Python Backwards Compatibility Policy
Theory
Burja, Samo. “Great Founder Theory”, 2020
Revisions
R3 (2025-12-29): Major revision incorporating Lakos/Berne feedback. Cut ~30% for conciseness. Added narrative hook, “Standard Library Graveyard” section, quantified 5-10% performance cost, Corentin Jabot quotes, self-censorship point. Expanded references.
R2 (2025-12-27): Reorganized for Substack publication.
R1 (2025-12-22): Removed WG21 paper metadata; repurposed from committee paper format.
R0 (2025-12-18): Initial draft as P9999R0.


Even granting the premise that the C++ standard cannot specify its environment, it isn’t obvious that this pushes us toward abandoning ABI stability. If anything, the absence of a specified environment raises the bar for changes that require global coordination. When a language has no reference runtime, no distribution channel, and no authority to enforce synchronized upgrades, the only leverage it has is whatever contracts the ecosystem already honors. The question isn’t whether the standard can define the environment. It can’t. The question is whether breaking ABI assumes a kind of control the language has never actually possessed.
Hi Vinnie,
I just went through P9999R0 and you raise some critical points--ones I think are overdue for our consideration. Here are some further points:
- The Cargo Advantage: It’s becoming impossible to ignore that Rust’s Cargo is a fundamental competitive advantage over C++. It’s not just about "convenience"; it’s about the velocity of the entire ecosystem. Cargo is a delight to use; and, in my opinion, one of Rust's strongest advantages. C++ _must_ create a similar tool to remain competitive.
- The Meritocracy Gap: We currently have no mechanism to embrace a true meritocracy for data structures. Why can't we have a "blessed" competition for things like Swiss Tables or better concurrency primitives? We need a way for the committee to provide "security and performance reviews" for external packages so they can be adopted with confidence without waiting a decade for an ISO seal. Empirically-tested data structures and packages should win the day, and receive the full recommendation from the C++ community. The strongest solutions should rise to the top. Move fast and promote strong packages. Move fast and improve things.
- Missing Modern Pillars: We are still struggling with basics while the industry has moved to IPC, rich thread pools, cross-platform inotify, C++ pandas/jupyter notebook kernels, distributed programming, and distributed concurrency. C++ should be leading here, but the lack of standardized "ecosystem tools" makes these tasks feel like reinventing the wheel every time.
- Delight as a Metric: Even the most advanced systems engineers appreciate a polished, easy-to-use interface. There’s a misconception that making things easy to use is "appealing to the lowest common denominator." It’s actually the opposite. It’s about making high-performance tools delightful to use so that we can focus on solving the actual problem, not the boilerplate.
- No Inroads: I want to teach my kids C++. I want it to be their first language. I don't want to teach them python as their first language. Where do I even start? Godbolt some examples? How do engineers start their careers in C++? The fragmentation and lack of "promoted" packages is also present in training materials. C++ should have an answer for this. Include people. Make C++ more inclusive by getting organized and promoting the strongest option. The C++ Guidelines are a great start, but we need more. We can do much better.
The "infinite pain" of technical debt you mentioned is real. Thanks for putting this on paper so clearly.
Best,
Steve