List of selected C++ Core Guidelines

P.1 Express ideas directly in code

P.2 Write in ISO Standard C++

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.6 What cannot be checked at compile-time should be checkable at run-time

P.7 Catch run-time errors early

P.8 Don’t leak any resources

P.9 Don’t waste time or space

P.10 Prefer immutable data to mutable data

P.11 Encapsulate messy constructs, rather than spreading through the code

P.12 Use supporting tools as appropriate

P.13 Use support libraries as appropriate

I.2 Avoid non-const global variables

I.3 Avoid singletons

I.13 Do not pass an array as a single pointer

I.27 For stable library ABI, consider the Pimpl idiom

F.4 If a function may have to be evaluated at compile-time, declare it constexpr

F.6 If your function may not throw, declare it noexcept

F.8 Prefer pure functions

F.15 Prefer simple and conventional ways of passing information

F.16 For “in” parameters, pass cheaply-copied types by value and others by reference to const

F.19 For “forward” parameters, pass by TP&& and only std::forward the parameter

F.17 For “in-out” parameters, pass by reference to non-const

F.20 For “out” output values, prefer return values to output parameters

F.21 To return multiple “out” values, prefer returning a struct or tuple

F.42 Return a T* to indicate a position (only)

F.44 Return a T& when copy is undesirable and “returning no object” isn’t needed

F.45 Don’t return a T&&

F.48 Don’t return std::move(local)

F.46 int is the return type for main()

F.50 Use a lambda when a function won’t do (to capture local variables, or to write a local function)

F.52 Prefer capturing by reference in lambdas that will be used locally, including passed to algorithms

F.53 Avoid capturing by reference in lambdas that will be used nonlocally, including returned, stored on the heap, or passed to another thread

F.51 Where there is a choice, prefer default arguments over overloading

F.55 Don’t use va_arg arguments

C.1 Organize related data into structures (structs or classes)

C.2 Use class if the class has an invariant; use struct if the data members can vary independently

C.3 Represent the distinction between an interface and an implementation using a class

C.4 Make a function a member only if it needs direct access to the representation of a class

C.5 Place helper functions in the same namespace as the class they support

C.7 Don’t define a class or enum and declare a variable of its type in the same statement

C.8 Use class rather than struct if any member is non-public

C.9 Minimize exposure of members

C.10 Prefer concrete types over class hierarchies

C.11 Make concrete types regular

C.20 If you can avoid defining any default operations, do

C.21 If you define or =delete any default operation, define or =delete them all

C.22 Make default operations consistent

C.41 A constructor should create a fully initialized object

C.42 If a constructor cannot construct a valid object, throw an exception

C.43 Ensure that a copyable (value type) class has a default constructor

C.45 Don’t define a default constructor that only initializes data members; use member initializers instead

C.46 By default, declare single-argument constructors explicit

C.47 Define and initialize member variables in the order of member declaration

C.48 Prefer in-class initializers to member initializers in constructors for constant initializers

C.49 Prefer initialization to assignment in constructors

C.51 Use delegating constructors to represent common actions for all constructors of a class

C.52 Use inheriting constructors to import constructors into a derived class that does not need further explicit initialization

C.67 A polymorphic class should suppress copying

C.30 Define a destructor if a class needs an explicit action at object destruction

C.31 All resources acquired by a class must be released by the class’s destructor

C.32 If a class has a raw pointer (T*) or reference (T&), consider whether it might be owning

C.33 If a class has an owning pointer member, define a destructor

C.35 A base class destructor should be either public and virtual, or protected and non-virtual

C.80 Use =default if you have to be explicit about using the default semantics

C.81 Use =delete when you want to disable default behavior (without wanting an alternative)

C.82 Don’t call virtual functions in constructors and destructors

C.86 Make == symmetric with respect to operand types and noexcept

C.87 Beware of == on base classes

C.120 Use class hierarchies to represent concepts with inherent hierarchical structure (only)

C.121 If a base class is used as an interface, make it an abstract class

C.122 Use abstract classes as interfaces when complete separation of interface and implementation is needed

C.126 An abstract class typically doesn’t need a constructor

C.128 Virtual functions should specify exactly one of virtual, override, or final

C.130 For making deep copies of polymorphic classes prefer a virtual clone function instead of copy construction/assignment

C.132 Don’t make a function virtual without reason

C.131 Avoid trivial getters and setters

C.133 Avoid protected data

C.134 Ensure all non-const data members have the same access level

C.129 When designing a class hierarchy, distinguish between implementation inheritance and interface inheritance

C.135 Use multiple inheritance to represent multiple distinct interfaces

C.138 Create an overload set for a derived class and its bases with using

C.140 Do not provide different default arguments for a virtual function and an overrider

C.146 Use dynamic_cast where class hierarchy navigation is unavoidable

C.147 Use dynamic_cast to a reference type when failure to find the required class is considered an error

C.148 Use dynamic_cast to a pointer type when failure to find the required class is considered a valid alternative

C.152 Never assign a pointer to an array of derived class objects to a pointer to its base

C.167 Use an operator for an operation with its conventional meaning

C.161 Use nonmember functions for symmetric operators

C.164 Avoid implicit conversion operators

C.162 Overload operations that are roughly equivalent

C.163 Overload only for operations that are roughly equivalent

C.168 Define overloaded operators in the namespace of their operands

C.180 Use unions to save memory

C.181 Avoid “naked” unions

C.182 Use anonymous unions to implement tagged unions

Enum.1 Prefer enumerations over macros

Enum.2 Use enumerations to represent sets of related named constants

Enum.3 Prefer enum classes over “plain” enums

Enum.5 Don’t use ALL_CAPS for enumerators

Enum.6 Avoid unnamed enumerations

Enum.7 Specify the underlying type of an enumeration only when necessary

Enum.8 Specify enumerator values only when necessary

R.1 Manage resources automatically using resource handles and RAII (Resource Acquisition Is Initialization)

R.3 A raw pointer (a T*) is non-owning

R.4 A raw reference (a T&) is non-owning

R.5 Prefer scoped objects, don’t heap-allocate unnecessarily

R.10 Avoid malloc() and free()

R.11 Avoid calling new and delete explicitly

R.12 Immediately give the result of an explicit resource allocation to a manager object

R.13 Perform at most one explicit resource allocation in a single expression statement

R.20 Use unique_ptr or shared_ptr to represent ownership

R.21 Prefer unique_ptr over shared_ptr unless you need to share ownership

R.22 Use make_shared() to make shared_ptrs

R.23 Use make_unique() to make unique_ptrs

R.24 Use std::weak_ptr to break cycles of shared_ptrs

R.30 Take smart pointers as parameters only to explicitly express lifetime semantics

R.37 Do not pass a pointer or reference obtained from an aliased smart pointer

ES.1 Prefer the standard library to other libraries and to “handcrafted code”

ES.2 Prefer suitable abstractions to direct use of language features

ES.5 Keep scopes small

ES.6 Declare names in for-statement initializers and conditions to limit scope

ES.7 Keep common and local names short, and keep uncommon and nonlocal names longer

ES.8 Avoid similar-looking names

ES.9 Avoid ALL_CAPS names

ES.10 Declare one name (only) per declaration

ES.11 Use auto to avoid redundant repetition of type names

ES.12 Do not reuse names in nested scopes

ES.20 Always initialize an object

ES.21 Don’t introduce a variable (or constant) before you need to use it

ES.22 Don’t declare a variable until you have a value to initialize it with

ES.23 Prefer the {}-initializer syntax

ES.26 Don’t use a variable for two unrelated purposes

ES.28 Use lambdas for complex initialization, especially of const variables

ES.40 Avoid complicated expressions

ES.41 If in doubt about operator precedence, parenthesize

ES.42 Keep use of pointers simple and straightforward

ES.45 Avoid “magic constants”; use symbolic constants

ES.55 Avoid the need for range checking

ES.47 Use nullptr rather than 0 or NULL

ES.61 Delete arrays using delete[] and non-arrays using delete

ES.65 Don’t dereference an invalid pointer

ES.43 Avoid expressions with undefined order of evaluation

ES.44 Don’t depend on order of evaluation of function arguments

ES.48 Avoid casts

ES.49 If you must use a cast, use a named cast

ES.50 Don’t cast away const

ES.78 Don’t rely on implicit fallthrough in switch statements

ES.79 Use default to handle common cases (only)

ES.100 Don’t mix signed and unsigned arithmetic

ES.101 Use unsigned types for bit manipulation

ES.102 Use signed types for arithmetic

ES.106 Don’t try to avoid negative values by using unsigned

ES.103 Don’t overflow

ES.104 Don’t underflow

ES.105 Don’t divide by zero

Per.7 Design to enable optimization

Per.10 Rely on the static type system

Per.11 Move computation from run time to compile-time

Per.19 Access memory predictably

CP.1 Assume that your code will run as part of a multi-threaded program

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 try to use volatile for synchronization

CP.9 Whenever feasible use tools to validate your concurrent code

CP.20 Use RAII, never plain lock()/unlock()

CP.21 Use std::lock() or std::scoped_lock to acquire multiple mutexes

CP.22 Never call unknown code while holding a lock (e.g., a callback)

CP.23 Think of a joining thread as a scoped container

CP.24 Think of a thread as a global container

CP.25 Prefer std::jthread over std::thread

CP.26 Don’t detach() a thread

CP.42 Don’t wait without a condition

CP.31 Pass small amounts of data between threads by value, rather than by reference or pointer

CP.32 To share ownership between unrelated threads use shared_ptr

CP.40 Minimize context switching

CP.41 Minimize thread creation and destruction

CP.43 Minimize time spent in a critical section

CP.44 Remember to name your lock_guards and unique_locks

CP.100 Don’t use lock-free programming unless you absolutely have to

CP.101 Distrust your hardware/compiler combination

CP.102 Carefully study the literature

E.3 Use exceptions for error handling only

E.14 Use purpose-designed user-defined types as exceptions (not built-in types)

E.15 Catch exceptions from a hierarchy by reference

E.13 Never throw while being the direct owner of an object

E.30 Don’t use exception specifications

E.31 Properly order your catch-clauses

Con.1 By default, make objects immutable

Con.2 By default, make member functions const

Con.3 By default, pass pointers and references to consts

Con.4 Use const to define objects with values that do not change after construction

Con.5 Use constexpr for values that can be computed at compile-time

T.1 Use templates to raise the level of abstraction of code

T.2 Use templates to express algorithms that apply to many argument types

T.3 Use templates to express containers and ranges

T.40 Use function objects to pass operations to algorithms

T.42 Use template aliases to simplify notation and hide implementation details

T.43 Prefer using over typedef for defining aliases

T.44 Use function templates to deduce class template argument types (where feasible)

T.46 Require template arguments to be at least Regular or SemiRegular

T.47 Avoid highly visible unconstrained templates with common names

T.48 If your compiler does not support concepts, fake them with enable_if

T.60 Minimize a template’s context dependencies

T.61 Do not over-parameterize members

T.62 Place non-dependent class template members in a non-templated base class

T.80 Do not naively templatize a class hierarchy

T.83 Do not declare a member function template virtual

T.140 Name all operations with potential for reuse

T.141 Use an unnamed lambda if you need a simple function object in one place only

T.143 Don’t write unintentionally nongeneric code

T.144 Don’t specialize function templates

CPL.1 Prefer C++ to C

CPL.2 If you must use C, use the common subset of C and C++, and compile the C code as C++

CPL.3 If you must use C for interfaces, use C++ in the calling code using such interfaces

SF.1 Use a .cpp suffix for code files and .h for interface files if your project doesn’t already follow another convention

SF.2 A .h file may not contain object definitions or non-inline function definitions

SF.5 A .cpp file must include the .h file(s) that defines its interface

SF.8 Use #include guards for all .h files

SF.9 Avoid cyclic dependencies among source files

SF.10 Avoid dependencies on implicitly #included names

SF.11 Header files should be self-contained

SF.6 Use using namespace directives for transition, for foundation libraries (such as std), or within a local scope (only)

SF.7 Don’t write using namespace at global scope in a header file

SF.20 Use namespaces to express logical structure

SF.21 Don’t use an unnamed (anonymous) namespace in a header

SF.22 Use an unnamed (anonymous) namespace for all internal/nonexported entities

SL.con.1 Prefer using STL array or vector instead of a C-array

SL.con.2 Prefer using STL vector by default unless you have a reason to use a different container

SL.con.3 Avoid bounds errors

SL.str.1 Use std::string to own character sequences

SL.str.2 Use std::string_view to refer to character sequences

SL.str.4 Use char* to refer to a single character

SL.str.5 Use std::byte to refer to byte values that do not necessarily represent characters

SL.str.12 Use the s suffix for string literals meant to be standard-library strings

SL.io.1 Use character-level input only when you have to

SL.io.2 When reading, always consider ill-formed input

SL.io.3 Prefer iostreams for I/O

SL.io.10 Unless you use printf-family functions call ios_base::sync_with_stdio(false)

SL.io.50 Avoid endl

A.1 Separate stable code from less stable code

A.2 Express potentially reusable parts as a library

A.4 There should be no cycles among libraries

NR.1 Don’t insist that all declarations should be at the top of a function

NR.2 Don’t insist to have only a single return-statement in a function

NR.3 Don’t avoid exceptions

NR.4 Don’t insist on placing each class declaration in its own source file

NR.5 Don’t use two-phase initialization

NR.6 Don’t place all cleanup actions at the end of a function and goto exit

NR.7 Don’t make all data members protected