Skip to content

About

Training reference: classical C++ through C++23 in 28 runnable projects, plus design/architecture patterns, static analysis and a compiler conformance test bed

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Latest commit

 

History

12 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Modern C++ Training

Learn modern C++ by reading and running code, not slides. 32 self-contained projects take you from the classical (pre-C++11) foundation to C++23, with guarded C++26 previews — plus design patterns, architecture patterns, data structures, static analysis and a Qt 6 tour.

CI

In every project the code is the course material: self-documenting names first, comments where they add insight, and every claim measured rather than asserted. Projects 01–31 are console programs; 32_qt_samples adds seven Qt 6 applications (screenshots).

Who it is for

You are Start at
New to C++ syntax 01_classical_cpp — reading_declarations.cpp decodes declarations token by token
Coming from C++98/03 02_cpp11_language → 03_cpp11_move_smart_pointers
Refreshing on C++17/20/23 05_cpp17 → 06_cpp20 → 07_cpp23
Here for tooling / static analysis 18_misra_deviations, 23_frontend_conformance, axivion/
Here for Qt 31_qt_bridge → 32_qt_samples

Prerequisites: a C++20-capable compiler (GCC 13+, Clang 18+, or MSVC 19.35+/VS 2022 17.5+), CMake 3.21+, and Ninja or Make. Two projects ask for more and skip themselves otherwise: 29_modules needs CMake 3.28+ with a modules-capable compiler, 31_qt_bridge needs Qt 6 Core and 32_qt_samples needs Qt 6.8+. Everything else builds without Qt at all.

Quick start

git clone https://github.com/MartinSch77/ModernCppTraining.git
cd ModernCppTraining
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build

./build/01_classical_cpp/01_classical_cpp    # start here
ctest --test-dir build                       # 30 tests: every sample builds AND runs

Each project is independent — build only the one you want:

cmake -S 06_cpp20 -B 06_cpp20/build -G Ninja && cmake --build 06_cpp20/build

Full build options, presets and the helper scripts are under Building.

Learning paths

The numbering is roughly a learning order, but this is the recommended route — four stages, each one usable as a stopping point:

  1. Foundations — 01 → 16 → 02 → 14 (syntax, the build pipeline, C++11, initialization & lifetime)
  2. Safe modern C++ — 03 → 09 → 04 → 05 (ownership & moves, OOP done right, C++14/17, RAII everywhere)
  3. Generic & idiomatic C++ — 08 → 06 → 24 → 25 → 19 (templates & concepts, C++20, data structures, algorithms & ranges, coroutines)
  4. Professional practice — 10 → 20 → 15 → 26 → 17 → 12 → 27 → 13 → 21 → 28 → 07 → 11 (concurrency, text/IO, UB & hardening, error handling, testing, patterns, fluent APIs, architecture, advanced techniques, embedded, C++23, analysis training — then 29–32 as the modules/modernization/Qt excursions)

Projects 11, 18, 22, 23, 29, 31 and 32 are specialty labs rather than curriculum steps — static analysis, compiler conformance, modules and Qt. Skip them on a first pass and come back when the subject is the tool, not the language.

If the syntax itself is new, 01_classical_cpp/src/reading_declarations.cpp decodes elementary declarations token by token — virtual double area() const = 0; is taken apart piece by piece there, and the same construct is annotated in place in inheritance_polymorphism.cpp.

Documentation

Document Answers
TUTORIAL.md Which C++ topic is taught where? A complete 23-chapter curriculum (core language, object/lifetime model, standard library, professional practice) mapped onto these projects — baseline C++23, C++26 strictly as a marked preview
USE_CASES.md What do I reach for? Typical use cases: task → modern C++ facility (ownership, errors, callbacks, data, concurrency, compile time), problem → design pattern in its modern C++ form, system shape → architecture pattern — plus what the language absorbed and when a pattern is over-engineering
STRUCTURE.md What is in which folder? Directory-by-directory map, the anatomy of a project folder, and which directories are generated
<project>/README.md What does this project teach, file by file? Every project has one — click any folder above
docs/ Doxygen setup: cd docs && doxygen Doxyfile builds a browsable HTML view of the annotated sources (comments preserved, directory and class diagrams included)
axivion/README_AXIVION.md Static-analysis integration, rule sets, and the findings write-ups
CHANGELOG.md What changed and when? Including the corrections — several published claims here were wrong until they were measured
CONTRIBUTING.md How do I add or fix something? The rules a sample must satisfy, and what to run before opening a pull request

Projects

The full catalogue, in numeric order. Every project folder has its own README explaining it file by file — and see Learning paths above if you would rather be told where to start than pick from a list.

Project Standard Content
01_classical_cpp C++98-era¹ reading declarations (what const means in each position, what = 0 means, references vs pointers, member initializer lists — start here if the syntax is new), RAII, rule of three (copy ctor/assignment), copy-and-swap, inheritance, virtual dtors, multiple inheritance + diamond/virtual bases, operator overloading, friends, exceptions
02_cpp11_language C++11¹ auto/decltype, nullptr, range-for, uniform init, enum class, lambdas, =default/=delete, override/final, delegating+inheriting ctors, variadic templates, constexpr, noexcept, UDLs, tuple/chrono/unordered containers
03_cpp11_move_smart_pointers C++11¹ rvalue refs, std::move, rule of five/zero, perfect forwarding, unique_ptr/shared_ptr/weak_ptr, custom deleters
04_cpp14 C++14 generic lambdas, init-capture, return type deduction, relaxed constexpr, variable templates, make_unique, integer_sequence, transparent comparators
05_cpp17 C++17 structured bindings, if-init, if constexpr, fold expressions, CTAD, optional/variant/any/string_view, filesystem, from_chars, guaranteed copy elision
06_cpp20 C++20 concepts, ranges + projections, coroutines (hand-written generator), operator<=>, designated init, span, format, consteval/constinit, bit_cast, endianness (std::endian + guarded C++23 byteswap), source_location
07_cpp23 C++23² deducing this, if consteval, expected, print, multidim operator[], static operator(), auto(x), zip, ranges::to, move_only_function, flat_map/mdspan
08_templates C++20 specialization (full/partial), NTTPs, template template params, SFINAE/enable_if, detection idiom, tag dispatch, CRTP, mixins, deduction guides, policy-based design
09_oop_principles C++17 four OOP pillars, SOLID (one demo each), NVI, PIMPL (real header/impl split), composition over inheritance
10_concurrency C++20² thread/jthread, mutex + RAII locks, condition_variable, async/future/promise/packaged_task, atomics, call_once, latch/barrier, thread-safe interface design (check-then-act race, monitor object, deadlock avoidance via scoped_lock)
11_axivion_challenge C++23² frontend torture test for Axivion cafeCC — see below
12_design_patterns C++20 GoF modernised: Singleton, Factory Method, Abstract Factory, Builder, Prototype, Adapter, Decorator, Composite, Facade, Strategy, Observer, Command, State, Visitor
13_architecture_patterns C++20 layered, hexagonal (ports & adapters), pipes & filters, event bus, SOA (contracts + service registry), SaaS (multi-tenancy, plans, quotas, metering)
14_initialization_lifetime C++20 the five initialization forms, aggregates + designated init, most vexing parse, storage durations, temporaries + lifetime extension, static-init-order fiasco + constinit, padding/alignment, value categories, the four casts
15_correctness_ub C++20 dangerous corners with broken AND fixed variants: dangling pointers, use-after-free, double delete, iterator invalidation, dangling string_view, signed overflow, unsigned wraparound, strict aliasing, null deref — broken code runs only with --run-broken under sanitizers
16_translation_and_linkage C++20 translation units, compilation vs. linking, static library and header-only INTERFACE library (incl. PRIVATE/PUBLIC/INTERFACE semantics), ODR, internal/external linkage, inline functions/variables, ADL, preprocessor, extern "C", linker-error anatomy, modules status
17_testing C++20 unit tests (self-contained mini-framework mapped to GoogleTest/Catch2/Qt Test), CTest integration, test doubles + dependency injection, parameterized/table-driven tests (mapped to TEST_P / GENERATE / Qt data-driven), exercise with tests
18_misra_deviations C++17² deliberate MISRA catalogue: compilable, runtime-safe code violating 164 of 179 MISRA C++:2023 rules (92 %) + 56 CWE rules — 1579 findings, every violation annotated with rule number and title; training material for static-analysis triage
19_coroutines_async C++20² co_await: the awaiter protocol traced live, Task<T> with continuations and symmetric transfer, a minimal scheduler, cooperative interleaving without threads, task composition, exceptions across co_await, cancellation; the std::generator demo builds as C++23 when the compiler has it and degrades honestly otherwise
20_text_and_io C++20 stream state, sticky manipulators, string streams, std::regex, char8_t/UTF-16/wchar_t + the library's Unicode limits, <random> done properly, the C++20 chrono calendar, complex, bitset, midpoint/lerp, floating-point reality
21_advanced_techniques C++20 type erasure (hidden hierarchy + manual vtable), custom allocators, std::pmr arenas, placement new + explicit destruction, std::launder, alignment, std::bind vs lambdas, std::ref/reference_wrapper, mem_fn/invoke, function_ref
22_compiler_stress C++20 compiler stresser: valid code that is expensive to COMPILE, one stressor per subsystem (instantiation, constant evaluator, overload resolution/subsumption, mangling/back end), scaled by -DTRAINING_STRESS_LEVEL=1..5 with measure.sh reporting time and peak memory per level
23_frontend_conformance C++23² conformance challenge: 67 probes + a generated 657-case stress corpus (single features, compositions, feature-guarded C++26, plus two hunt families that drill around confirmed defects), one per translation unit, plus probe.sh producing a PASS/FAIL/SKIP/CRASH matrix across g++/clang++/cafeCC/cl — findings include cafeCC exiting 0 on failed compilations (writes IR anyway — a CI hazard that had faked earlier PASS verdicts), two distinct cafeCC crash defects (one on valid C++20: mangling sizeof... of an init-capture pack — array bound, template argument or nested re-capture — under static_assert → lower_name.c:7380, boundary measured probe by probe), GCC ICEs on the uncaptured-array family, alias CTAD (P1814) broken in both Clang 18 and cafeCC, captured structured bindings in constexpr (P1091) rejected by Clang and cafeCC, GCC 13 rejecting = default constexpr virtual dtors, and — from the stress corpus — a silent miscompare of defaulted <=> over a member array (its boundary now fully mapped: ordering is equal for any array pair, equality correct), folds and closures rejected in template-argument positions, GCC's unimplemented noexcept_expr/requires_expr mangling (cafeCC gets it right), a constexpr recursion limit of 256 (vs. the recommended 512), and lambdas inside constraints evaluating wrongly
24_data_structures C++20 data structures by hand, then from the library: singly and doubly linked lists (sentinel ring, O(1) splice, the recursive destructor trap), a binary search tree with all four traversals and its degenerate case, separate chaining vs. linear probing, a binary heap in a vector, graphs (BFS/DFS, topological sort, Dijkstra, union-find) — each one followed by the std:: container that replaces it, plus the complexity and invalidation table
25_algorithms_and_ranges C++20 the algorithms: the <algorithm>/<numeric> catalogue by intent (search, binary search, sort/stable_sort, partition, rotate, erase-remove, accumulate's init-type trap, set operations), strict weak ordering and how to test a comparator, projections and what they cost, the iterator categories, insert/stream/reverse/move iterators, the C++20 sentinel, and views: laziness measured, filter_view caching, std::ranges::dangling
26_error_handling C++23² error handling done properly: choosing the channel (optional / expected+monadic ops / error codes / exceptions with context), the three exception-safety guarantees with a verified-torn assignment and its copy-and-swap fix, why moves must be noexcept (vector growth measured: 28 copies vs 28 moves), scope guards with commit/rollback
27_fluent_api C++20 fluent APIs: method chaining and why the &&-qualified overload matters (0 copies vs 2, measured), typestate builders where build() does not exist until the required fields are set (proved with concepts), an expression-template predicate DSL that is fully constexpr, operator| pipelines written from scratch with stage composition, fluent wrappers over types you do not own — and when fluency is the wrong choice
28_embedded_cpp C++20 modern C++ on a microcontroller — which constructions and why: consteval tables, strong types, templates instead of virtual (vptr cost measured), if constexpr board variants; life without the heap (span, fixed-capacity vector, pmr on a std::array); what volatile does and does not promise, typed MMIO registers, <bit>/bit_cast; ISR safety (is_always_lock_free, release/acquire, a lock-free SPSC queue, RAII interrupt lock); -fno-exceptions/-fno-rtti, constinit, and the free/budgeted/excluded cost table
29_modules C++20 modules: every unit kind once — primary interface (export module, module linkage), a partition (:stats), an implementation unit whose edits never recompile importers — with honest toolchain gating (CMake ≥ 3.28 + Ninja + Clang 16+/MSVC 19.34+/GCC 14+; GCC 13 prints SKIPPED — only -fmodules-ts)
30_modernization C++20 the legacy→modern playbook: four refactoring steps with legacy and modern side by side producing identical printed results — ownership (new[]/rule-of-three → rule of zero), interfaces (out-params/void* callbacks → optional/lambdas), types (macros/C arrays → constexpr/std::array, incl. the MAX(v++,0) double-evaluation trap live), loops (index arithmetic → named algorithms) — each step naming the clang-tidy/analysis checks that find the legacy form
31_qt_bridge C++20 modern C++ meets Qt (needs Qt 6 Core, honest SKIP without): copy-on-write vs move semantics proven with constData() identity incl. the accidental-detach trap, UTF-16 vs UTF-8 sizes of one word, QObject parent-tree deletions counted by destructors vs unique_ptr, compile-checked connects, and moc's template limit with the signal-base idiom
32_qt_samples C++23–26² the Qt 6 technology tour (needs Qt ≥ 6.8, verified with Qt 6.11.1; honest SKIP per missing module): seven self-contained apps — Qt Widgets 2D scope painted in paintEvent over one shared QAbstractTableModel, Qt Quick telemetry board (QML_ELEMENT list model, C++ bindable QProperty, Shapes, states/transitions), Qt Quick 3D with the mesh generated in C++ (QQuick3DGeometry, PBR), live-scrolling Qt Charts, Qt Concurrent future pipelines (mappedReduced, QPromise from std::jthread, whenAll), an in-process TCP echo built on QDataStream read transactions, and Qt SQL chained through std::expected::and_then — built as C++26 (cxx_std_26) where the toolchain has it, incl. feature-guarded = delete("reason") and std::add_sat

¹ built as C++14 because that is the lowest language mode MSVC supports. ² C++23/20 library features are guarded with feature-test macros (__cpp_... / __cpp_lib_... from <version>) and degrade to an informative message — the projects compile on toolchains with partial support (e.g. GCC 13 lacks deducing this and std::print).

Toolchains and warning policy

Verified toolchains: GCC 13 and Clang 18, Debug and Release, both warning-free with -Wall -Wextra -Wpedantic — enforced in CI with -DTRAINING_WERROR=ON, which turns warnings into errors. Written to compile with MSVC 19.35+ / Visual Studio 2022 17.5+ (/W4 /permissive-).

Two projects opt out of -Werror, because being diagnosable is their content: 15_correctness_ub (GCC's -Wuse-after-free fires on deliberate use-after-free) and 23_frontend_conformance (probes that provoke diagnostics on purpose). The opt-out is per target and carries its reason in the CMakeLists.

Building

All projects at once (from the repository root):

# Linux / GCC or Clang
cmake -S . -B build -G Ninja -DCMAKE_BUILD_TYPE=Debug
cmake --build build
./build/05_cpp17/05_cpp17          # run any sample

# Windows / MSVC (from a VS developer prompt)
cmake -S . -B build
cmake --build build --config Debug
build\05_cpp17\Debug\05_cpp17.exe

Or via CMake presets — the named configurations IDEs pick up automatically (debug, release, werror, sanitizers, qt):

cmake --preset debug && cmake --build --preset debug
cmake --preset qt    # needs: export QT_KIT=$HOME/Qt/6.11.1/gcc_64
ctest --preset debug

Every project is also a standalone CMake project (used by the per-project Axivion analysis):

cmake -S 06_cpp20 -B 06_cpp20/build -G Ninja
cmake --build 06_cpp20/build

The C++23 projects (07, 11, 26) are skipped automatically if the compiler has no C++23 mode.

Or use the scripts:

./build_no_axivion.sh    # configure + build + run all samples + ctest,
                         # no Axivion Suite required at all
./build_all.sh           # the same, plus an Axivion analysis per project

build_no_axivion.sh is configurable through the environment: BUILD_DIR, BUILD_TYPE=Release, CXX=clang++, SANITIZERS=ON, SKIP_RUN=1. Verified with GCC 13 Debug and Clang 18 Release.

Run the tests with CTest — 30 in a default build: a smoke test per sample (every project must build and run with exit code 0) plus project 17's unit tests. A Qt-enabled build (--preset qt) registers the 32_qt_samples apps as well, for 37:

ctest --test-dir build
ctest --test-dir build -R smoke     # only the smoke tests

Compiler stress measurement (project 22 — default level 1 keeps normal builds fast; higher levels are the experiment):

cmake -S . -B build -DTRAINING_STRESS_LEVEL=3   # raise the load
./22_compiler_stress/measure.sh 1 4             # time + memory per level
cmake --build build --target 22_stress_report    # GCC per-phase timing

Sanitizer build (used by project 15's --run-broken mode):

cmake -S . -B build-asan -G Ninja -DTRAINING_SANITIZERS=ON
cmake --build build-asan
./build-asan/15_correctness_ub/15_correctness_ub --run-broken

C++20 modules are intentionally not included: compiler/CMake support is still too uneven across GCC/MSVC to make a reliable training sample; revisit once import std; works out of the box on both.

Axivion Suite

Verified against Axivion Suite 7.12.3: all configurations pass axivion_ci --validate, and cafeCC builds run end-to-end.

  • Each project has a complete config directory <project>/axivion/ (axivion_config.json + ci_config.json + rule layer + generated GCC compiler profile + start_analysis.sh), created by axivion/make_axivion_configs.sh.
  • Rule sets (rule_config_cpp.json): MisraC++2023 + CWE, with StaticSemanticAnalysis enabled (default parameters) for the global cross-TU rule variants. Note: MISRA C++'s newest standard is 2023; the "2025" release is MISRA C:2025, which does not apply to C++ code.
  • The installation integrity check is switched off per project via Project-GlobalOptions/advanced/check_installation_integrity: false.
  • Run one analysis: ./05_cpp17/axivion/start_analysis.sh (append --only_build for a cafeCC-only check, --validate for a config check). Run all: phase 3 of ./build_all.sh.
  • axivion/README_AXIVION.md documents the flow, the configuration choices, and a real frontend finding that project 11 caught (cafeCC 7.12.3 rejects libstdc++ 13's <type_traits> in C++23 mode).

Challenging a frontend (projects 11, 23)

23_frontend_conformance is the systematic version: one probe per feature, so a rejection is attributable rather than buried. ./23_frontend_conformance/probe.sh -s 23 prints the matrix. Measured here: cafeCC 7.12.3 accepts 26 of 28 feature probes at C++23 (including deducing this, which GCC 13 lacks); alias-template CTAD (P1814) is broken in both Clang 18 and cafeCC; and the sharpest finding is in the driver: cafeCC diagnoses ordinary compile errors, then exits 0 and writes an IR object anyway — probe.sh therefore judges cafeCC by its diagnostics, not its exit code.

Dense corner cases (project 11)

11_axivion_challenge is deliberately dense, standard-conforming C++20/23: string NTTPs, lambdas in unevaluated contexts as unique tag types, compile-time std::sort, constexpr virtual dispatch, UDL character packs with digit separators, deducing this, and a coroutine generator that models std::ranges::input_range feeding a views pipeline. GCC and Clang accept all of it warning-free — any diagnostic an analysis frontend produces there is a tooling finding, not a code bug.

Contributing

Corrections are the most valuable contribution here — especially measurements that disagree with the ones published in this repository, and compiler results from toolchains other than GCC 13 / Clang 18 / cafeCC 7.12.3 on Linux. See CONTRIBUTING.md for the rules every sample has to satisfy, and CODE_OF_CONDUCT.md for behaviour expectations.

Two projects contain unsafe or non-conforming code on purpose — 15_correctness_ub and 18_misra_deviations; SECURITY.md explains what is worth reporting and what is working as intended.

Licence

MIT — use the samples in your own code, teaching or otherwise.

About

Training reference: classical C++ through C++23 in 28 runnable projects, plus design/architecture patterns, static analysis and a compiler conformance test bed

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages