git:20260509.966244f to git:20260510.fe97ca3

50 added, 50 removed. Audit A to A.

---
name: programming-cpp
title: "C++ Development"
- description: "Modern C++20/23 conventions, RAII, smart pointers, CMake, sanitizers, and safe systems programming best practices. Auto-activates in C++ projects."
+ description: "Modern C++ architecture, RAII, value semantics, and library choices that survive a decade. Auto-activates in C++ projects."
license: Apache-2.0
- compatibility: "Requires C++ compiler (clang++ or g++) and a build system (CMake, Meson, or Make)."
+ compatibility: "Requires a C++20-capable compiler and a build system (CMake, Meson)."
domains: developer
rules:
- file(CMakeLists.txt)
- file(meson.build)
- file(conanfile.txt)
- file(conanfile.py)
- file(vcpkg.json)
- content(c++)
- content(cpp)
---
- ## Conventions
+ ## Mental model
- - Modern C++ — default to C++20, C++23 features when project opts in
- - RAII everywhere — all resources managed by constructor/destructor
- - Smart pointers — `std::unique_ptr` by default, `std::shared_ptr` only for shared ownership
- - NEVER raw `new`/`delete` — use `std::make_unique`, `std::make_shared`, containers
- - `const` correctness — `const` on member functions, parameters, locals wherever possible
- - Rule of 0 — prefer no custom destructor/copy/move; if you customize one, implement all 5
- - `[[nodiscard]]` on functions where ignoring return is likely a bug
- - `override` on ALL virtual overrides
+ Modern C++ is a value-semantic language with optional escape hatches. The maintainable subset is small: RAII for resources, values by default, references for non-owning views, smart pointers when ownership is dynamic, and `std::` containers/algorithms over hand-rolled equivalents. Most C++ misery comes from importing patterns from C (manual new/delete), Java (everything heap-allocated and polymorphic), or pre-C++11 codebases.
- ## Memory Safety
+ ## Ownership and resource management
- - `std::unique_ptr` for exclusive ownership, `std::shared_ptr` for shared
- - `std::string_view` / `std::span` for non-owning references — watch lifetime
- - Never return references or `string_view` to locals or temporaries
- - Always initialize variables — C++ does NOT zero-initialize locals
- - `noexcept` on move constructors, move assignments, and destructors
+ - RAII handles every resource: memory, files, sockets, locks, GPU handles — wrap raw resources in a type whose destructor releases them
+ - `std::unique_ptr<T>` is the default for dynamic ownership; `std::shared_ptr<T>` only when ownership is genuinely shared (and document why)
+ - `std::make_unique` / `std::make_shared` — raw `new` is a code smell outside placement-new contexts
+ - Rule of zero by default: if all members are RAII types, the compiler-generated special members are correct
+ - Rule of five only when managing a resource directly; mark move operations `noexcept` or containers fall back to copies
- ## Modern Features
+ ## Value semantics over polymorphism
- - Structured bindings: `auto [key, value] = pair;`
- - `std::optional` for nullable, `std::variant` for sum types
- - `std::expected` (C++23) for error handling without exceptions
- - Concepts over SFINAE: `template<std::integral T>`
- - Ranges: `std::ranges::sort(vec)`
- - `std::format` / `std::print` over iostream
- - `if constexpr` for compile-time branching
- - `std::jthread` over `std::thread` (auto-joining)
+ - Pass small types by value, large types by `const&`, sink parameters by value (then move into place)
+ - Return by value — RVO and move semantics make it cheap; out-parameters are a 1990s habit
+ - Reach for inheritance only when runtime polymorphism is actually needed (plugin boundaries, framework hooks); virtual functions are not the default
+ - `std::variant` + `std::visit` for closed sum types — far easier to reason about than a class hierarchy
+ - `std::optional<T>` for "maybe a value"; `std::expected<T, E>` (C++23) for "value or error"
- ## Error Handling
+ ## Lifetime hazards
- - Exceptions for truly exceptional cases
- - `std::expected` (C++23) or Result-type for expected errors
- - Error codes at API boundaries where exceptions can't cross
- - RAII ensures cleanup regardless of error path
+ - `std::string_view` and `std::span<T>` are non-owning — never return them referring to locals or temporaries
+ - A reference or pointer outlives the referent silently; prefer values in struct members unless the borrow is structurally clear
+ - A moved-from object is valid but unspecified — assign or destroy before reading
+ - Polymorphic base classes need a virtual destructor; without one, `delete base_ptr` is undefined behavior
+ - Object slicing on by-value polymorphic parameters silently drops derived state — take `const Base&`
- ## Build System (CMake)
+ ## Modern features worth standardizing on
- - Modern target-based: `target_link_libraries`, `target_include_directories`
- - No global `include_directories()` or `link_libraries()`
- - FetchContent or vcpkg/conan for dependencies
- - `CMAKE_EXPORT_COMPILE_COMMANDS=ON` for tooling
- - Minimum CMake 3.21+
+ - Concepts (`template<std::integral T>` or `requires`) — replace SFINAE, dramatically improve diagnostics
+ - `std::ranges` and views for composable algorithms over containers
+ - `if constexpr`, `consteval`, `constinit` for compile-time computation and enforcement
+ - `std::format` / `std::print` (C++23) — typesafe, much faster than iostreams
+ - `std::jthread` with `std::stop_token` for cooperative cancellation; `std::thread` is legacy
+ - `[[nodiscard]]` on factories and error-returning functions
- ## Testing & Sanitizers
+ ## Concurrency
- - Google Test or Catch2 — both integrate with ctest
- - Test with sanitizers enabled (ASan + UBSan)
- - ASan: `-fsanitize=address` — buffer overflow, use-after-free, leaks
- - UBSan: `-fsanitize=undefined` — signed overflow, null deref
- - TSan: `-fsanitize=thread` — data races
- - Compiler warnings: `-Wall -Wextra -Wpedantic -Werror`
+ - Prefer a higher-level model (task system, executors, message passing) over raw threads and locks
+ - A `std::mutex` should protect a tiny critical section — long-held locks are a design failure
+ - `std::atomic<T>` for lock-free counters and flags; sequential consistency unless profiling proves a weaker order safe
+ - Pass `const&` data across thread boundaries; mutable shared state needs synchronization or single-owner discipline
+ - Async I/O via a library (Asio, libuv, executors proposal) — don't hand-roll event loops
- ## Common Pitfalls
+ ## Project layout and build
- - Dangling references: returning `string_view`/`const&` to temporaries
- - Use-after-move: accessing objects after `std::move`
- - Signed/unsigned mixing: `int` vs `size_t` comparison bugs
- - Missing `virtual` destructor on polymorphic base classes
- - Object slicing: passing derived by value to base parameter
+ - CMake with modern target-based commands (`target_link_libraries`, `target_include_directories`); avoid global `include_directories` and `link_libraries`
+ - `PUBLIC` / `PRIVATE` / `INTERFACE` propagation matters — use them deliberately so consumers inherit only what they need
+ - Headers under `include/<project>/...`, sources under `src/`; one logical component per directory
+ - Dependencies via vcpkg, Conan, or `FetchContent` — not git submodules
+ - Compile with high warnings on (`-Wall -Wextra -Wpedantic`, `/W4`) and treat warnings as errors in CI
+
+ ## Ecosystem defaults
+
+ - Containers and algorithms: `std::` first, Abseil/Folly for what's missing
+ - Logging: `spdlog`
+ - Testing: GoogleTest or Catch2
+ - HTTP server/client: cpp-httplib for simple cases, Beast/Asio for scale
+ - JSON: `nlohmann/json` for ergonomics, `simdjson` for performance
+ - CLI parsing: CLI11 or `argparse`