C++ (C++23)
Analysis against C++23 (ISO/IEC 14882:2024). Older C++ standards have substantially worse coupling properties; C++23 represents the best of what modern C++ offers.
This assessment applies the coupling account — coupling as access to a shared resource, and the coupling floor [1], [2] — and the knowledge-embodiment theorem [3]; the language is assessed against its official reference [4].
1 Assessment
This page assesses C++ in five parts: core coupling — what the language’s own constructs force; ecosystem coupling — what the surrounding platform, tooling, and community cost; engineering readiness — whether the artifact can be reproduced, verified, observed, worked in quickly, connected to other languages, and governed openly; the profile — the unscored facts a score cannot express; and the scoring detail — the mechanism marks and the arithmetic behind every score. A ✅ bullet marks a construct that helps. A ⚠️ or ❌ bullet marks a cost; the scoring model does not price every cost named here.
Core coupling 50/100 · Ecosystem coupling 61/100 · Engineering readiness 65/100
Note: The score reflects C++23 with modern idioms (smart pointers ≥ raw pointers,
std::expected≥ exceptions, modules ≥ headers, concepts ≥ unconstrained templates). Teams stuck on older standards or pre-modern coding styles will experience significantly worse coupling properties.
1.1 Core coupling
1.1.1 Type
- ✅ Templates enable generic programming: Decouple algorithms from types
- ✅ RAII separates resource management: Constructor acquires, destructor releases
- ✅ Concepts (C++20) constrain templates: Clean error messages, explicit constraints decouple interface from implementation
- ✅ Modules (C++20/23) decouple compilation:
importreplaces#includeand removes header coupling, though headers remain for code not yet migrated - ✅ Deducing this (C++23): Eliminates const/non-const overload duplication
- ⚠️ Multiple inheritance still available: Diamond problem possible (though
finalspecifier helps) - ⚠️ Template metaprogramming still complex: Despite concepts, advanced templates couple to expertise
- ❌ Macros couple: Preprocessor doesn’t respect scope (though modules reduce header macro issues)
1.1.2 Construction
- ✅ Constructors separate: Copy, move, default constructors are distinct
- ✅ Move semantics decouple: Transfer ownership without copying
- ✅ Designated initializers (C++20): Named initialization decouples from parameter order
- ❌ Constructors can throw: Exception during construction couples cleanup
- ⚠️ Two-phase initialization still possible: Though discouraged by modern idioms
- ❌ Global state common: Static variables, singletons (not enforced away)
1.1.3 Memory
- ✅ RAII decouples resource management: Destructors run deterministically, no GC needed
- ✅ Smart pointers eliminate manual delete:
unique_ptr,shared_ptrare idiomatic (manualnew/deleteis a code smell in modern C++) - ✅
std::span/std::mdspan(C++20/23): Non-owning one- and multi-dimensional views decouple algorithms from container types and memory layout - ✅ Explicit lifetime management (C++23):
std::start_lifetime_asfor safe low-level patterns - ⚠️
shared_ptrhas atomic ref counting cost: And circular references needweak_ptr - ⚠️ Raw pointer escape hatches exist: A raw pointer and a reference can alias the same object with no owner stated, though idiomatic C++23 uses spans and smart pointers (M4)
- ✅ No GC, deterministic: Full control over allocation timing
- ❌ Undefined behavior still exists: Buffer overruns, null derefs possible when using raw pointers/functions
1.1.4 Error
- ✅
std::expectedandstd::optionalmonadic operations (C++23):and_then,transform,or_elsemake error and absence handling composable and type-safe - ❌ Exceptions still exist and remain unchecked:
std::expectedis idiomatic and preferred, but exceptions are still in the language and used in the standard library - ✅
std::stacktrace(C++23): Provides unwinding visibility, addressing historical debugging pain - ✅
std::variant: Type-safe discriminated union for error discrimination
1.1.5 Concurrency
- ⚠️ Shared mutable state default: Mutexes required, though lock-free patterns are achievable
- ✅
std::jthread(C++20): Auto-joining threads with cancellation viastd::stop_token - ✅
std::latch,std::barrier,std::semaphore(C++20): Higher-level synchronization primitives - ✅
std::generator(C++23): Coroutine-based generator decouples producer from consumer - ❌ Data races are undefined behavior: No compile-time prevention (unlike Rust)
- ⚠️ Memory model still complex: Atomic operations require deep understanding
1.1.6 Platform
- ✅ No vendor owner: The language is standardised by ISO/IEC and implemented independently by GCC, Clang, and MSVC, so no single vendor controls the standard
- ✅ No required platform runtime: A program compiles to native code and needs no C++-specific virtual machine or vendor service
- ✅ Every mainstream target is supported: Compilers and standard libraries exist for Linux, macOS, Windows, and embedded and bare-metal targets
- ✅ One source base for several implementations: Code that stays in the portable subset compiles under GCC, Clang, and MSVC, so the project is not bound to one compiler
1.1.7 Trade-offs (not coupling)
- RAII vs. Exceptions: Exception during construction couples cleanup to unwinding, may leak
- Zero-Cost Abstractions vs. Compile Time: Templates (and now concepts + modules) couple build speed to abstraction usage
- C Compatibility vs. Modern Features: Legacy C patterns can couple to modern C++ code when used
- Standard Versions: C++11/14/17/20/23 couple codebases to version choices; modern C++ is largely additive since C++11
- Safety vs. Performance:
std::spanbounds-checking is opt-in; raw pointer escape hatches remain for performance
1.2 Ecosystem coupling
1.2.1 Performance
- ✅ Zero-overhead abstraction: Pay only for what you use
- ✅ No GC: Deterministic, predictable
- ✅ Manual control: Full optimization control
- ✅ Compile-time computation: Constexpr (expanded in C++23 — now covers
std::bitset,std::unique_ptr,std::optional,std::variant,std::string,cmathfunctions) - ⚠️ Binary bloat: Templates can explode size (mitigated by explicit instantiations, LTO)
1.2.2 Ecosystem
- ⚠️ No standard package manager: CMake + Conan/vcpkg are de facto standards, but fragmentation persists
- ⚠️ Compilation speed: Templates + concepts + modules improve but still slower than Go/Rust
- ✅ Excellent IDE support: CLion, Visual Studio, VSCode with clangd — all support C++23
- ✅ Vast library ecosystem: Decades of libraries, Boost, Qt, etc.
- ⚠️ Build system convergence improving: CMake dominates; vcpkg/Conan converging
1.2.3 Evolution
- ✅ Additive since C++11: Code from C++11+ largely compiles under C++23 (removed features are rare and deprecated first)
- ⚠️ Legacy C++98 burden: Old codebases still exist, but not a language-level problem for new code
- ✅ 3-year standard cadence: Predictable evolution (C++23 → C++26 → C++29)
- ⚠️ Feature complexity grows: Each standard adds more to learn
1.2.4 Domain
- ✅ Excellent for systems: Performance critical code
- ✅ Great for games: Industry standard
- ✅ Strong for embedded: (with care, constexpr + templates help)
- ✅ Good for high-performance: Finance, scientific computing
- ⚠️ Not for web: Rare use case
1.2.5 Learning
- ❌ Very steep: Massive language surface area accumulated over 40+ years
- ❌ C++23 idioms are learnable but the full language is not: Smart pointers,
expected, ranges, modules add to surface - ❌ Undefined behavior: Still exists and requires understanding to avoid
- ✅ Excellent resources: Effective Modern C++, C++ Core Guidelines, cppreference
1.2.6 Syntax
- ✅ Modules reduce header boilerplate:
import std;replaces many#includes - ⚠️ Template syntax:
<>remains noisy, though concepts improve readability - ✅ Designated initializers: Named member initialization improves readability
- ✅
auto, structured bindings (C++17): Reduce verbosity
1.2.7 Testing
- ✅ GTest, Catch2, doctest: Mature frameworks
- ✅ Static analyzers: Clang-Tidy, CppCheck, and MSVC Code Analysis find some undefined behaviour before runtime, though no analyzer is sound or complete
- ✅ Profilers: Excellent tooling (perf, VTune, Instruments)
- ✅
std::stacktrace(C++23): Runtime stacktrace capture improves debugging - ⚠️ Template errors: Concepts (C++20) dramatically improve error messages, but edge cases remain
1.2.8 Security
- ⚠️ Memory safety depends on idiom:
std::span,std::mdspan, smart pointers are memory-safe; raw pointers and pointer arithmetic are not - ✅ Smart pointers + RAII eliminate most memory bugs in modern C++:
unique_ptrprevents use-after-free, double-free - ✅ AddressSanitizer, MemorySanitizer: Compiler instrumentation catches memory bugs at test time
- ❌ Unsafe escape hatches remain:
reinterpret_cast, pointer arithmetic, manualnew/deletestill compile (Q1)
1.3 Engineering readiness
1.3.1 Reproducibility
- ❌ Builds are not byte-reproducible by default.
- ❌ Dependencies resolve without a lock by default.
1.3.2 Supply chain
- ⚠️ Published packages carry no signed provenance by default.
1.3.3 Observability
- ⚠️ The standard library has no structured logging.
1.3.4 Feedback
- ❌ A change pays a slow build.
1.3.5 Interop
- ✅ No charged mechanism in this dimension.
1.3.6 Governance
- ✅ No charged mechanism in this dimension.
1.4 Profile
Unscored, and kept separate from every score on purpose. The profile records what a mechanism price cannot express: what a running program costs, how well a coding model and an agent work in the language, and whether a safety-critical path exists.
1.4.1 Energy and cost
What this axis asks: what does a running program cost in energy and time?
1.4.2 AI and agent readiness
What this axis asks: how well does a coding model and an agent work in this language?
1.4.3 Safety certification
What this axis asks: is there a certified or standards-qualified path to a safety-critical system?
- ✅ A standards-qualified toolchain or an established safety standard covers this language.
1.5 Scoring detail
1.5.1 Core coupling Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Type | T1 |
dynamic no static check | — | 0 |
T2 |
weak static | ✅ | −8 | |
T3 |
dynamic/duck dispatch | — | 0 | |
T4 |
no sum types | — | 0 | |
T5 |
no generics | — | 0 | |
T6 |
no namespaces | — | 0 | |
T7 |
type erasure/reflection | — | 0 | |
T8 |
operator overloading | ✅ | −4 | |
T9 |
inheritance required for polymorphism | — | 0 | |
T10 |
no null-safety | ✅ | −8 | |
T11 |
no module boundary | — | 0 | |
T12 |
no effect tracking | ✅ | −8 | |
T13 |
async/sync colouring | — | 0 | |
T14 |
unchecked foreign boundary | ✅ | −4 | |
| Type subtotal | −15 (−32) | |||
| Construction | C1 |
global mutable state by default | — | 0 |
C2 |
no immutability by default | ✅ | −4 | |
C3 |
constructors coupled to allocation | — | 0 | |
C4 |
no constructor/validation | — | 0 | |
| Construction subtotal | −4 | |||
| Memory | M1 |
manual deallocation | — | 0 |
M2 |
GC non-deterministic pauses | — | 0 | |
M3 |
no RAII | — | 0 | |
M4 |
no ownership model | ✅ | −8 | |
| Memory subtotal | −8 | |||
| Error | Er1 |
no structured path | — | 0 |
Er2 |
unchecked exceptions only | ✅ | −8 | |
Er3 |
errors ignored/non-exhaustive | — | 0 | |
Er4 |
no composable result type | — | 0 | |
| Error subtotal | −8 | |||
| Concurrency | K1 |
GIL/no parallel | — | 0 |
K2 |
shared mutable state default | ✅ | −8 | |
K3 |
manual sync no race detection | ✅ | −8 | |
K4 |
no structured concurrency | — | 0 | |
K5 |
no primitives | — | 0 | |
| Concurrency subtotal | −15 (−16) | |||
| Platform | P1 |
cannot function outside one vendor/domain | — | 0 |
P2 |
single-vendor but other uses | — | 0 | |
P3 |
legacy/discontinued platform | — | 0 | |
| Platform subtotal | 0 |
Core Dimensions score: 50/100 — total cost = −50 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
1.5.2 Ecosystem coupling Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Performance | R1 |
interpreted no AOT/JIT | — | 0 |
R2 |
GC pauses | — | 0 | |
R3 |
JIT warmup | — | 0 | |
| Performance subtotal | 0 | |||
| Ecosystem | Ec1 |
no package manager | ✅ | −4 |
Ec2 |
fragmented tooling | ✅ | −8 | |
Ec3 |
small/niche/discontinued | — | 0 | |
Ec4 |
proprietary | — | 0 | |
Ec5 |
platform-locked tooling | — | 0 | |
Ec6 |
monoculture | — | 0 | |
Ec7 |
legacy burden | — | 0 | |
| Ecosystem subtotal | −12 | |||
| Evolution | V1 |
breaking changes | — | 0 |
V2 |
frozen | — | 0 | |
V3 |
discontinued | — | 0 | |
V4 |
glacial standards | — | 0 | |
| Evolution subtotal | 0 | |||
| Domain | D1 |
domain/vendor lock-in | — | 0 |
D2 |
poor domain fit | — | 0 | |
D3 |
framework forces architecture | — | 0 | |
| Domain subtotal | 0 | |||
| Learning | L1 |
steep | ✅ | −8 |
L2 |
large surface/multi-paradigm | ✅ | −4 | |
L3 |
sparse docs | — | 0 | |
| Learning subtotal | −12 | |||
| Syntax | S1 |
verbose | — | 0 |
S2 |
inconsistent | — | 0 | |
S3 |
punctuation-heavy | — | 0 | |
| Syntax subtotal | 0 | |||
| Testing | G1 |
weak tooling | — | 0 |
G2 |
poor debugging | — | 0 | |
G3 |
runtime-only errors | — | 0 | |
| Testing subtotal | 0 | |||
| Security | Q1 |
memory-unsafe | ✅ | −15 |
Q2 |
dynamic/weak typing risk | — | 0 | |
Q3 |
known vulnerability/injection | — | 0 | |
| Security subtotal | −15 |
Meta Dimensions score: 61/100 — total cost = −39 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
1.5.3 Engineering readiness Dimensions
| Dimension | Mechanism | Description | Present | Cost |
|---|---|---|---|---|
| Reproducibility | Y1 |
build not reproducible | ✅ | −8 |
Y2 |
dependencies not pinned | ✅ | −8 | |
| Reproducibility subtotal | −15 (−16) | |||
| Supply chain | Y3 |
no signed provenance | ✅ | −8 |
Y4 |
registry without namespacing | — | 0 | |
| Supply chain subtotal | −8 | |||
| Observability | Y5 |
no stable tracing standard | — | 0 |
Y6 |
no structured logging | ✅ | −4 | |
| Observability subtotal | −4 | |||
| Feedback | Y7 |
slow build | ✅ | −8 |
Y8 |
no fast in-editor analysis | — | 0 | |
| Feedback subtotal | −8 | |||
| Interop | Y9 |
no foreign-function interface | — | 0 |
Y10 |
no portable target | — | 0 | |
| Interop subtotal | 0 | |||
| Governance | Y11 |
single-vendor governance | — | 0 |
Y12 |
closed specification | — | 0 | |
| Governance subtotal | 0 |
Readiness Dimensions score: 65/100 — total cost = −35 (per-dimension subtotals are capped at −15; an uncapped cost is shown in parentheses where the cap bites).
Verdict: C++23 provides substantially better coupling than its reputation suggests. Modules decouple compilation units. std::expected provides composable error handling. Concepts eliminate template error message hell. std::span/std::mdspan provide safe non-owning views. Smart pointers are idiomatic and eliminate manual new/delete. Main strength: RAII + zero-cost abstractions + deterministic performance. Main weakness: the language retains unsafe escape hatches (raw pointers, macros, exceptions) and new features coexist with old ones, meaning teams must enforce modern idiom discipline. C++23 with Core Guidelines and static analysis is a different language from C++98.
The formal development. The definitions, theorems, and proofs behind this page are published in:
- On the Nature of Coupling: All Coupling as Shared Resource Access — Zenodo, 2026 — doi:10.5281/zenodo.21925122
- Coupling as a Consequence of Cohesion: Decomposition, the Coupling Floor, and Domain-Imposed Coupling — Zenodo, 2026 — doi:10.5281/zenodo.21925201
- On the Nature of Cohesion: Cohesion as Correctness Relative to a Partitioning Rule — Zenodo, 2026 — doi:10.5281/zenodo.21703193