C++ ABI Constraints on std::regex and std::string_view

The C++ standardization committee’s commitment to Application Binary Interface (ABI) stability preserves binary compatibility between compiled translation units, but it also prevents meaningful architectural improvements to existing standard library components. Both std::regex and std::string_view demonstrate the cost of this trade-off. Proposals to overhaul the notoriously poor performance of std::regex and refine the memory layout or calling conventions of std::string_view faced insurmountable opposition because modifying internal member variables, object sizes, or function signatures alters the compiled machine code interface, breaking interoperability with previously compiled binary libraries.

An ABI dictates low-level details that source code does not explicitly define, including the memory layout of classes (member variable order and alignment), class sizes, calling conventions (such as how arguments are passed in registers versus on the stack), and name mangling. When a standard library implementation (such as GNU libstdc++, LLVM libc++, or Microsoft MSVC STL) ships a class, its ABI becomes fixed. If a new version of the standard alters that class's data layout, linking newly compiled code with an older precompiled dynamic library (.so or .dll) causes undefined behavior, often manifesting as memory corruption, silent data corruption, or segmentation faults.

The primary issue with std::regex stems from its design and initial implementation choices dating back to TR1 (Technical Report 1) and C++11. Many standard implementations of std::regex are orders of magnitude slower than alternatives such as Google's RE2, PCRE, or modern compile-time regular expression engines. The performance issues arise from suboptimal state-machine representation, recursive parsing techniques, and excessive heap allocations for match state tracking.

Fixing std::regex requires completely replacing its internal representation, changing member data structures, or altering the layout of helper types like std::match_results. Doing so changes the sizeof(std::regex) and the offsets of its members. Because standard vendors refused to introduce an ABI break that would invalidate entire software ecosystems relying on precompiled code, the committee could not adopt structural fixes. As a result, std::regex remains virtually unfixable in place; any real performance replacement would have to be standardized under a distinct name (such as a hypothetical std::regex2).

While std::string_view was introduced much later in C++17, it encountered a different set of ABI constraints related to hardware efficiency and calling conventions. Conceptually, std::string_view consists of two pieces of data: a pointer to a character array and a size (std::size_t). The System V AMD64 ABI (used by Linux and macOS) dictates how two-word structures are passed into CPU registers. The exact specification of trivial copyability, structure layout, and destructor behavior impacts whether a std::string_view can be passed efficiently in two CPU registers or must be passed indirectly through memory.

During and after the standardization of std::string_view, proposals were discussed regarding alternative representations, null-termination aware variants, and optimizations for passing views to functions. Furthermore, inconsistencies across compilers in how small, aggregate-like classes are passed in registers created friction. Changing std::string_view's member layout, adding flags (such as whether the referenced string is null-terminated), or changing its triviality would alter whether callers and callees agree on how registers are utilized. If a library compiled under an older standard passed a string_view through memory while a newer caller passed it in registers, function calls would immediately fail.

The standoff reached a turning point during the WG21 Prague meeting in 2020, where the C++ committee took an indicative vote against breaking ABI for the C++23 cycle. This decision cemented the reality that standard library types cannot mutate their binary footprint to gain performance or modern ergonomics once released. For both std::regex and std::string_view, ABI preservation means that historical design flaws and performance bottlenecks must be bypassed through new types rather than repaired in the existing abstractions.