| name | cpp-coding-standards |
| description | C++ coding standards based on the C++ Core Guidelines (isocpp.github.io). Use when writing, reviewing, or refactoring C++ code to enforce modern, safe, and idiomatic practices. |
| origin | MCC |
C++ Coding Standards (C++ Core Guidelines)
Comprehensive coding standards for modern C++ (C++17/20/23) derived from the C++ Core Guidelines. Enforces type safety, resource safety, immutability, and clarity.
When to Use
- Writing new C++ code (classes, functions, templates)
- Reviewing or refactoring existing C++ code
- Making architectural decisions in C++ projects
- Enforcing consistent style across a C++ codebase
- Choosing between language features (e.g.,
enum vs enum class, raw pointer vs smart pointer)
When NOT to Use
- Non-C++ projects
- Legacy C codebases that cannot adopt modern C++ features
- Embedded/bare-metal contexts where specific guidelines conflict with hardware constraints (adapt selectively)
Cross-Cutting Principles
- RAII everywhere (P.8, R.1, E.6, CP.20): Bind resource lifetime to object lifetime
- Immutability by default (P.10, Con.1-5, ES.25): Start with
const/constexpr; mutability is the exception
- Type safety (P.4, I.4, ES.46-49, Enum.3): Use the type system to prevent errors at compile time
- Express intent (P.3, F.1, NL.1-2, T.10): Names, types, and concepts should communicate purpose
- Minimize complexity (F.2-3, ES.5, Per.4-5): Simple code is correct code
- Value semantics over pointer semantics (C.10, R.3-5, F.20, CP.31): Prefer returning by value and scoped objects
For detailed code examples for each section below, see detailed-examples.md.
Philosophy & Interfaces (P., I.)
| Rule | Summary |
|---|
| P.1 | Express ideas directly in code |
| P.3 | Express intent |
| P.4 | Ideally, a program should be statically type safe |
| P.5 | Prefer compile-time checking to run-time checking |
| P.8 | Don't leak any resources |
| P.10 | Prefer immutable data to mutable data |
| I.1 | Make interfaces explicit |
| I.2 | Avoid non-const global variables |
| I.4 | Make interfaces precisely and strongly typed |
| I.11 | Never transfer ownership by a raw pointer or reference |
| I.23 | Keep the number of function arguments low |
Functions (F.*)
| Rule | Summary |
|---|
| F.1 | Package meaningful operations as carefully named functions |
| F.2 | A function should perform a single logical operation |
| F.3 | Keep functions short and simple |
| F.4 | If a function might be evaluated at compile time, declare it constexpr |
| F.6 | If your function must not throw, declare it noexcept |
| F.8 | Prefer pure functions |
| F.16 | For "in" parameters, pass cheaply-copied types by value and others by const& |
| F.20 | For "out" values, prefer return values to output parameters |
| F.21 | To return multiple "out" values, prefer returning a struct |
| F.43 | Never return a pointer or reference to a local object |
Anti-patterns: Returning T&& (F.45), va_arg variadics (F.55), capturing by reference in cross-thread lambdas (F.53), returning const T inhibiting moves (F.49).
Classes & Class Hierarchies (C.*)
| Rule | Summary |
|---|
| C.2 | Use class if invariant exists; struct if data members vary independently |
| C.9 | Minimize exposure of members |
| C.20 | Rule of Zero: avoid defining default operations if possible |
| C.21 | Rule of Five: if you define any copy/move/destructor, handle them all |
| C.35 | Base class destructor: public virtual or protected non-virtual |
| C.41 | A constructor should create a fully initialized object |
| C.46 | Declare single-argument constructors explicit |
| C.67 | A polymorphic class should suppress public copy/move |
| C.128 | Virtual functions: specify exactly one of virtual, override, or final |
Anti-patterns: Virtual calls in ctors/dtors (C.82), memset/memcpy on non-trivial types (C.90), const/reference data members suppressing move (C.12).
Resource Management (R.*)
| Rule | Summary |
|---|
| R.1 | Manage resources automatically using RAII |
| R.3 | A raw pointer (T*) is non-owning |
| R.5 | Prefer scoped objects; don't heap-allocate unnecessarily |
| R.10 | Avoid malloc()/free() |
| R.11 | Avoid calling new and delete explicitly |
| R.20 | Use unique_ptr or shared_ptr to represent ownership |
| R.21 | Prefer unique_ptr over shared_ptr unless sharing ownership |
Anti-patterns: Naked new/delete (R.11), malloc()/free() in C++ (R.10), multiple allocations in one expression (R.13).
Expressions & Statements (ES.*)
| Rule | Summary |
|---|
| ES.5 | Keep scopes small |
| ES.20 | Always initialize an object |
| ES.23 | Prefer {} initializer syntax |
| ES.25 | Declare objects const or constexpr unless modification is intended |
| ES.28 | Use lambdas for complex initialization of const variables |
| ES.45 | Avoid magic constants; use symbolic constants |
| ES.46 | Avoid narrowing/lossy arithmetic conversions |
| ES.47 | Use nullptr rather than 0 or NULL |
| ES.48 | Avoid casts |
| ES.50 | Don't cast away const |
Anti-patterns: Uninitialized variables, C-style casts, casting away const, mixing signed/unsigned, reusing names in nested scopes.
Error Handling (E.*)
| Rule | Summary |
|---|
| E.1 | Develop an error-handling strategy early in a design |
| E.2 | Throw an exception to signal that a function can't perform its task |
| E.6 | Use RAII to prevent leaks |
| E.12 | Use noexcept when throwing is impossible or unacceptable |
| E.14 | Use purpose-designed user-defined types as exceptions |
| E.15 | Throw by value, catch by reference |
| E.16 | Destructors, deallocation, and swap must never fail |
Anti-patterns: Throwing built-in types (E.14), catching by value (E.15), empty catch blocks, exceptions for flow control (E.3).
Constants & Immutability (Con.*)
| Rule | Summary |
|---|
| Con.1 | By default, make objects immutable |
| Con.2 | By default, make member functions const |
| Con.3 | By default, pass pointers and references to const |
| Con.4 | Use const for values that don't change after construction |
| Con.5 | Use constexpr for values computable at compile time |
Concurrency & Parallelism (CP.*)
| Rule | Summary |
|---|
| CP.2 | Avoid data races |
| CP.3 | Minimize explicit sharing of writable data |
| CP.4 | Think in terms of tasks, rather than threads |
| CP.8 | Don't use volatile for synchronization |
| CP.20 | Use RAII, never plain lock()/unlock() |
| CP.21 | Use std::scoped_lock to acquire multiple mutexes |
| CP.22 | Never call unknown code while holding a lock |
| CP.42 | Don't wait without a condition |
| CP.44 | Remember to name your lock_guards and unique_locks |
Anti-patterns: volatile for sync (CP.8), detaching threads (CP.26), unnamed lock guards (CP.44), lock-free without expertise (CP.100).
Templates & Generic Programming (T.*)
| Rule | Summary |
|---|
| T.1 | Use templates to raise the level of abstraction |
| T.10 | Specify concepts for all template arguments |
| T.11 | Use standard concepts whenever possible |
| T.13 | Prefer shorthand notation for simple concepts |
| T.43 | Prefer using over typedef |
| T.120 | Use template metaprogramming only when you really need to |
| T.144 | Don't specialize function templates (overload instead) |
Standard Library, Enumerations, Naming, Performance
| Rule | Summary |
|---|
| SL.con.2 | Prefer std::vector by default |
| SL.str.2 | Use std::string_view to refer to character sequences |
| SL.io.50 | Avoid endl (use '\n') |
| Enum.3 | Prefer enum class over plain enum |
| Enum.5 | Don't use ALL_CAPS for enumerators |
| SF.7 | Don't write using namespace at global scope in a header |
| SF.8 | Use #include guards for all .h files |
| NL.10 | Prefer underscore_style names |
| Per.11 | Move computation from run time to compile time |
| Per.19 | Access memory predictably |
Quick Reference Checklist
Before marking C++ work complete:
Reference Files
- detailed-examples.md -- Complete code examples for every guideline section (functions, classes, RAII, error handling, concurrency, templates, naming, and more)