plugins/modern-cpp/skills/modern-cpp/SKILL.md
Guides C++ code toward modern idioms (C++20/23/26). Use when writing new C++ code, modernizing legacy patterns, or working on security-critical C++. Replaces raw pointers with smart pointers, SFINAE with concepts, printf with std::print, error codes with std::expected.
npx skillsauth add trailofbits/skills modern-cppInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Guide for writing modern C++ using C++20, C++23, and C++26 idioms. Focuses on patterns that eliminate vulnerability classes and reduce boilerplate, with a security emphasis from Trail of Bits.
| Avoid | Use Instead | Why |
|-------|-------------|-----|
| new/delete | std::make_unique, std::make_shared | Eliminates leaks, double-free |
| Raw owning pointers | std::unique_ptr, std::shared_ptr | RAII ownership semantics |
| C arrays (int arr[N]) | std::array<int, N> | Bounds-aware, value semantics |
| Pointer + length params | std::span<T> | Non-owning, bounds-checkable |
| printf / sprintf | std::format, std::print | Type-safe, no buffer overflow |
| C-style casts (int)x | static_cast<int>(x) | Explicit intent, auditable |
| #define constants | constexpr variables | Scoped, typed, debuggable |
| SFINAE / enable_if | Concepts + requires | Readable constraints and errors |
| Error codes + out params | std::expected<T, E> | Composable, type-safe errors |
| union | std::variant | Type-safe, no silent UB |
| Raw mutex.lock()/unlock() | std::scoped_lock | Exception-safe, no deadlocks |
| std::thread | std::jthread | Auto-join, stop token support |
| assert() macro | contract_assert (C++26) | Visible to tooling, configurable |
| Manual CRTP | Deducing this (C++23) | Simpler, no template boilerplate |
| Macro code generation | Reflection (C++26) | Zero-overhead, composable |
See anti-patterns.md for the full table (30+ patterns).
What are you doing?
|
+-- Writing new C++ code?
| +-- Use modern idioms by default (C++20/23)
| +-- Choose the newest standard your compiler supports
| +-- See Feature Tiers below
|
+-- Modernizing existing code?
| +-- Start with Tier 1 (C++20/23) replacements
| +-- Prioritize by security impact (memory > types > style)
| +-- See anti-patterns.md for the migration table
|
+-- Security-critical code?
| +-- Enable compiler hardening flags (see below)
| +-- Enable hardened libc++ mode
| +-- Run sanitizers in CI
| +-- See safe-idioms.md and compiler-hardening.md
|
+-- Using C++26 features?
+-- Reflection: YES, plan for it (GCC 16+)
+-- Contracts: cautiously, for new API boundaries
+-- std::execution: wait for ecosystem maturity
+-- See cpp26-features.md
Features are ranked by practical usability today, not by standard version.
| Feature | Replaces | Standard |
|---------|----------|----------|
| Concepts + requires | SFINAE, enable_if | C++20 |
| Ranges + views | Raw iterator loops | C++20 |
| std::span<T> | Pointer + length | C++20 |
| std::format | sprintf, iostream chains | C++20 |
| Three-way comparison <=> | Manual comparison operators | C++20 |
| std::jthread | std::thread + manual join | C++20 |
| Designated initializers | Positional struct init | C++20 |
| std::expected<T,E> | Error codes, exceptions at boundaries | C++23 |
| std::print / std::println | printf, std::cout << | C++23 |
| Deducing this | CRTP, const/non-const duplication | C++23 |
| std::flat_map | std::map for read-heavy use | C++23 |
| Monadic std::optional | Nested if-checks on optionals | C++23 |
See cpp20-features.md and cpp23-features.md.
These improve safety without changing your C++ standard version:
-D_FORTIFY_SOURCE=3, -fstack-protector-strong, -ftrivial-auto-var-init=zero-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST for ~0.3% overhead bounds-checking-Wall -Wextra -Wpedantic -WerrorSee compiler-hardening.md.
Reflection is the single most transformative C++26 feature. It eliminates:
to_json)GCC 16 (April 2026) has reflection merged. Plan new code to benefit from it.
pre/post/contract_assert) — Better than assert(), but no virtual function support and limited compiler support. Adopt cautiously for new API boundaries.See cpp26-features.md.
-Wall -Wextra -Wpedantic -Werror
-D_FORTIFY_SOURCE=3
-fstack-protector-strong
-fstack-clash-protection
-ftrivial-auto-var-init=zero
-fPIE -pie
-Wl,-z,relro,-z,now
-Wunsafe-buffer-usage
-D_LIBCPP_HARDENING_MODE=_LIBCPP_HARDENING_MODE_FAST
Google deployed this across Chrome and their server fleet: ~0.3% overhead, 1000+ bugs found, 30% reduction in production segfaults.
See compiler-hardening.md for the full guide.
| Rationalization | Why It's Wrong |
|----------------|----------------|
| "It compiles without warnings" | Warnings depend on which flags you enable. Add -Wall -Wextra -Wpedantic. |
| "ASan is too slow for production" | Use GWP-ASan for sampling-based production detection (~0% overhead). |
| "We only use safe containers" | Iterator invalidation and unchecked optional access are still exploitable. |
| "Smart pointers are slower" | std::unique_ptr has zero overhead vs raw pointers. Measure before claiming. |
| "Our code doesn't have memory bugs" | Google found 1000+ bugs when enabling hardened libc++. So did everyone else. |
| "C++26 features aren't available yet" | C++20/23 features are. Hardening flags work on any standard. Start there. |
| "Modern C++ is harder to read" | std::expected is more readable than checking error codes across 5 out-params. |
std::span over pointer + length for function parametersstd::expected for functions that can fail with typed errorsconstexpr / consteval where possible (UB-free by design)[[nodiscard]] when ignoring the return value is likely a bugstd::variant over union, enum class over enumdevelopment
Reviews a code target by launching a panel of specialist auditor agents and merging their reports. Use when asked to run a panel review.
development
Reviews the current branch's changes against its base branch as a pull request: correctness of new and modified code, test coverage for it, and documentation accuracy. Use when asked to review a branch, a diff, or a pull request.
tools
Runs an autonomous review-and-fix improvement loop over a Claude Code skill until a review comes back clean, with a cross-round findings ledger, escalation when fixes stop converging, and a mechanical scope guard. Reviews are performed by the plugin-dev skill-reviewer agent. Use to fix skill quality issues, iteratively refine a skill, or resume a loop after an escalation ('fix my skill', 'improve this skill until it passes review', 'skill improvement loop'). NOT for a one-time review — use the plugin-dev skill-reviewer agent directly.
tools
Runs an autonomous review-and-fix improvement loop over the current branch's changes until a PR review comes back clean, scoped mechanically to the directories the branch touched. Reviews are performed by an installed PR-review skill (default: pr-review-toolkit's review-pr). Use to fix review findings on a branch before opening or updating a pull request ('clean up this branch', 'fix this PR until review passes', 'run review-and-fix on my changes'). NOT for a one-time review — run the PR-review skill directly.