August 13, 2018
Editors:
This is a living document under continuous improvement. Had it been an open-source (code) project, this would have been release 0.8. Copying, use, modification, and creation of derivative works from this project is licensed under an MIT-style license. Contributing to this project requires agreeing to a Contributor License. See the accompanying LICENSE file for details. We make this project available to "friendly users" to use, copy, modify, and derive from, hoping for constructive input.
Comments and suggestions for improvements are most welcome. We plan to modify and extend this document as our understanding improves and the language and the set of available libraries improve. When commenting, please note the introduction that outlines our aims and general approach. The list of contributors is here.
Problems:
- The sets of rules have not been completely checked for completeness, consistency, or enforceability.
- Triple question marks (???) mark known missing information
- Update reference sections; many pre-C++11 sources are too old.
- For a more-or-less up-to-date to-do list see: To-do: Unclassified proto-rules
You can read an explanation of the scope and structure of this Guide or just jump straight in:
- In: Introduction
- P: Philosophy
- I: Interfaces
- F: Functions
- C: Classes and class hierarchies
- Enum: Enumerations
- R: Resource management
- ES: Expressions and statements
- Per: Performance
- CP: Concurrency and parallelism
- E: Error handling
- Con: Constants and immutability
- T: Templates and generic programming
- CPL: C-style programming
- SF: Source files
- SL: The Standard Library
Supporting sections:
- A: Architectural ideas
- NR: Non-Rules and myths
- RF: References
- Pro: Profiles
- GSL: Guidelines support library
- NL: Naming and layout rules
- FAQ: Answers to frequently asked questions
- Appendix A: Libraries
- Appendix B: Modernizing code
- Appendix C: Discussion
- Appendix D: Supporting tools
- Glossary
- To-do: Unclassified proto-rules
You can sample rules for specific language features:
- assignment: regular types -- prefer initialization -- copy -- move -- other operations -- default
class: data -- invariant -- members -- helpers -- concrete types -- ctors, =, and dtors -- hierarchy -- operatorsconcept: rules -- in generic programming -- template arguments -- semantics- constructor:
invariant --
establish invariant --
throw-- default -- not needed --explicit-- delegating --virtual - derived
class: when to use -- as interface -- destructors -- copy -- getters and setters -- multiple inheritance -- overloading -- slicing --dynamic_cast - destructor: and constructors -- when needed? -- may not fail
- exception:
errors --
throw-- for errors only --noexcept-- minimizetry-- what if no exceptions? for: range-for and for -- for and while -- for-initializer -- empty body -- loop variable -- loop variable type ???- function: naming -- single operation -- no throw -- arguments -- argument passing -- multiple return values -- pointers -- lambdas
inline: small functions -- in headers- initialization:
always --
prefer
{}-- lambdas -- in-class initializers -- class members -- factory functions - lambda expression: when to use
- operator: conventional -- avoid conversion operators -- and lambdas
public,private, andprotected: information hiding -- consistency --protectedstatic_assert: compile-time checking -- and conceptsstruct: for organizing data -- use if no invariant -- no private memberstemplate: abstraction -- containers -- conceptsunsigned: and signed -- bit manipulationvirtual: interfaces -- notvirtual-- destructor -- never fail
You can look at design concepts used to express the rules:
- assertion: ???
- error: ???
- exception: exception guarantee (???)
- failure: ???
- invariant: ???
- leak: ???
- library: ???
- precondition: ???
- postcondition: ???
- resource: ???
This document is a set of guidelines for using C++ well. The aim of this document is to help people to use modern C++ effectively. By "modern C++" we mean C++17, C++14, and C++11. In other words, what would you like your code to look like in 5 years' time, given that you can start now? In 10 years' time?
The guidelines are focused on relatively high-level issues, such as interfaces, resource management, memory management, and concurrency. Such rules affect application architecture and library design. Following the rules will lead to code that is statically type safe, has no resource leaks, and catches many more programming logic errors than is common in code today. And it will run fast -- you can afford to do things right.
We are less concerned with low-level issues, such as naming conventions and indentation style. However, no topic that can help a programmer is out of bounds.
Our initial set of rules emphasizes safety (of various forms) and simplicity. They may very well be too strict. We expect to have to introduce more exceptions to better accommodate real-world needs. We also need more rules.
You will find some of the rules contrary to your expectations or even contrary to your experience. If we haven't suggested you change your coding style in any way, we have failed! Please try to verify or disprove rules! In particular, we'd really like to have some of our rules backed up with measurements or better examples.
You will find some of the rules obvious or even trivial. Please remember that one purpose of a guideline is to help someone who is less experienced or coming from a different background or language to get up to speed.
Many of the rules are designed to be supported by an analysis tool. Violations of rules will be flagged with references (or links) to the relevant rule. We do not expect you to memorize all the rules before trying to write code. One way of thinking about these guidelines is as a specification for tools that happens to be readable by humans.
The rules are meant for gradual introduction into a code base. We plan to build tools for that and hope others will too.
Comments and suggestions for improvements are most welcome. We plan to modify and extend this document as our understanding improves and the language and the set of available libraries improve.
This is a set of core guidelines for modern C++, C++17, C++14, and C++11, taking likely future enhancements and ISO Technical Specifications (TSs) into account. The aim is to help C++ programmers to write simpler, more efficient, more maintainable code.
Introduction summary:
- In.target: Target readership
- In.aims: Aims
- In.not: Non-aims
- In.force: Enforcement
- In.struct: The structure of this document
- In.sec: Major sections
The rules in this section are very general.
Philosophy rules summary:
- 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
Philosophical rules are generally not mechanically checkable. However, individual rules reflecting these philosophical themes are. Without a philosophical basis, the more concrete/specific/checkable rules lack rationale.
An interface is a contract between two parts of a program. Precisely stating what is expected of a supplier of a service and a user of that service is essential. Having good (easy-to-understand, encouraging efficient use, not error-prone, supporting testing, etc.) interfaces is probably the most important single aspect of code organization.
Interface rule summary:
- I.1: Make interfaces explicit
- I.2: Avoid non-
constglobal variables - I.3: Avoid singletons
- I.4: Make interfaces precisely and strongly typed
- I.5: State preconditions (if any)
- I.6: Prefer
Expects()for expressing preconditions - I.7: State postconditions
- I.8: Prefer
Ensures()for expressing postconditions - I.9: If an interface is a template, document its parameters using concepts
- I.10: Use exceptions to signal a failure to perform a required task
- I.11: Never transfer ownership by a raw pointer (
T*) or reference (T&) - I.12: Declare a pointer that must not be null as
not_null - I.13: Do not pass an array as a single pointer
- I.22: Avoid complex initialization of global objects
- I.23: Keep the number of function arguments low
- I.24: Avoid adjacent unrelated parameters of the same type
- I.25: Prefer abstract classes as interfaces to class hierarchies
- I.26: If you want a cross-compiler ABI, use a C-style subset
- I.27: For stable library ABI, consider the Pimpl idiom
- I.30: Encapsulate rule violations
See also:
- F: Functions
- C.concrete: Concrete types
- C.hier: Class hierarchies
- C.over: Overloading and overloaded operators
- C.con: Containers and other resource handles
- E: Error handling
- T: Templates and generic programming
A function specifies an action or a computation that takes the system from one consistent state to the next. It is the fundamental building block of programs.
It should be possible to name a function meaningfully, to specify the requirements of its argument, and clearly state the relationship between the arguments and the result. An implementation is not a specification. Try to think about what a function does as well as about how it does it. Functions are the most critical part in most interfaces, so see the interface rules.
Function rule summary:
Function definition rules:
- 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 may have to be evaluated at compile time, declare it
constexpr - F.5: If a function is very small and time-critical, declare it inline
- F.6: If your function may not throw, declare it
noexcept - F.7: For general use, take
T*orT&arguments rather than smart pointers - F.8: Prefer pure functions
- F.9: Unused parameters should be unnamed
Parameter passing expression rules:
- 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.17: For "in-out" parameters, pass by reference to non-
const - F.18: For "will-move-from" parameters, pass by
X&&andstd::movethe parameter - F.19: For "forward" parameters, pass by
TP&&and onlystd::forwardthe parameter - 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.60: Prefer
T*overT&when "no argument" is a valid option
Parameter passing semantic rules:
- F.22: Use
T*orowner<T*>to designate a single object - F.23: Use a
not_null<T>to indicate that "null" is not a valid value - F.24: Use a
span<T>or aspan_p<T>to designate a half-open sequence - F.25: Use a
zstringor anot_null<zstring>to designate a C-style string - F.26: Use a
unique_ptr<T>to transfer ownership where a pointer is needed - F.27: Use a
shared_ptr<T>to share ownership
- F.42: Return a
T*to indicate a position (only) - F.43: Never (directly or indirectly) return a pointer or a reference to a local object
- F.44: Return a
T&when copy is undesirable and "returning no object" isn't needed - F.45: Don't return a
T&& - F.46:
intis the return type formain() - F.47: Return
T&from assignment operators - F.48: Don't
return std::move(local)
Other function rules:
- F.50: Use a lambda when a function won't do (to capture local variables, or to write a local function)
- F.51: Where there is a choice, prefer default arguments over overloading
- 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.54: If you capture
this, capture all variables explicitly (no default capture) - F.55: Don't use
va_argarguments
Functions have strong similarities to lambdas and function objects.
See also: C.lambdas: Function objects and lambdas
A class is a user-defined type, for which a programmer can define the representation, operations, and interfaces. Class hierarchies are used to organize related classes into hierarchical structures.
Class rule summary:
- C.1: Organize related data into structures (
structs orclasses) - C.2: Use
classif the class has an invariant; usestructif 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
classrather thanstructif any member is non-public - C.9: Minimize exposure of members
Subsections:
- C.concrete: Concrete types
- C.ctor: Constructors, assignments, and destructors
- C.con: Containers and other resource handles
- C.lambdas: Function objects and lambdas
- C.hier: Class hierarchies (OOP)
- C.over: Overloading and overloaded operators
- C.union: Unions
Enumerations are used to define sets of integer values and for defining types for such sets of values.
There are two kind of enumerations, "plain" enums and class enums.
Enumeration rule summary:
- 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.4: Define operations on enumerations for safe and simple use
- Enum.5: Don't use
ALL_CAPSfor 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
This section contains rules related to resources. A resource is anything that must be acquired and (explicitly or implicitly) released, such as memory, file handles, sockets, and locks. The reason it must be released is typically that it can be in short supply, so even delayed release may do harm. The fundamental aim is to ensure that we don't leak any resources and that we don't hold a resource longer than we need to. An entity that is responsible for releasing a resource is called an owner.
There are a few cases where leaks can be acceptable or even optimal: If you are writing a program that simply produces an output based on an input and the amount of memory needed is proportional to the size of the input, the optimal strategy (for performance and ease of programming) is sometimes simply never to delete anything. If you have enough memory to handle your largest input, leak away, but be sure to give a good error message if you are wrong. Here, we ignore such cases.
-
Resource management rule summary:
- R.1: Manage resources automatically using resource handles and RAII (Resource Acquisition Is Initialization)
- R.2: In interfaces, use raw pointers to denote individual objects (only)
- 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.6: Avoid non-
constglobal variables
-
Allocation and deallocation rule summary:
- R.10: Avoid
malloc()andfree() - R.11: Avoid calling
newanddeleteexplicitly - 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.14: ??? array vs. pointer parameter
- R.15: Always overload matched allocation/deallocation pairs
- R.10: Avoid
-
- R.20: Use
unique_ptrorshared_ptrto represent ownership - R.21: Prefer
unique_ptrovershared_ptrunless you need to share ownership - R.22: Use
make_shared()to makeshared_ptrs - R.23: Use
make_unique()to makeunique_ptrs - R.24: Use
std::weak_ptrto break cycles ofshared_ptrs - R.30: Take smart pointers as parameters only to explicitly express lifetime semantics
- R.31: If you have non-
stdsmart pointers, follow the basic pattern fromstd - R.32: Take a
unique_ptr<widget>parameter to express that a function assumes ownership of awidget - R.33: Take a
unique_ptr<widget>¶meter to express that a function reseats thewidget - R.34: Take a
shared_ptr<widget>parameter to express that a function is part owner - R.35: Take a
shared_ptr<widget>¶meter to express that a function might reseat the shared pointer - R.36: Take a
const shared_ptr<widget>¶meter to express that it might retain a reference count to the object ??? - R.37: Do not pass a pointer or reference obtained from an aliased smart pointer
- R.20: Use
Expressions and statements are the lowest and most direct way of expressing actions and computation. Declarations in local scopes are statements.
For naming, commenting, and indentation rules, see NL: Naming and layout.
General rules:
- ES.1: Prefer the standard library to other libraries and to "handcrafted code"
- ES.2: Prefer suitable abstractions to direct use of language features
Declaration rules:
- 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_CAPSnames - ES.10: Declare one name (only) per declaration
- ES.11: Use
autoto 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.24: Use a
unique_ptr<T>to hold pointers - ES.25: Declare an object
constorconstexprunless you want to modify its value later on - ES.26: Don't use a variable for two unrelated purposes
- ES.27: Use
std::arrayorstack_arrayfor arrays on the stack - ES.28: Use lambdas for complex initialization, especially of
constvariables - ES.30: Don't use macros for program text manipulation
- ES.31: Don't use macros for constants or "functions"
- ES.32: Use
ALL_CAPSfor all macro names - ES.33: If you must use macros, give them unique names
- ES.34: Don't define a (C-style) variadic function
Expression rules:
- ES.40: Avoid complicated expressions
- ES.41: If in doubt about operator precedence, parenthesize
- ES.42: Keep use of pointers simple and straightforward
- ES.43: Avoid expressions with undefined order of evaluation
- ES.44: Don't depend on order of evaluation of function arguments
- ES.45: Avoid "magic constants"; use symbolic constants
- ES.46: Avoid narrowing conversions
- ES.47: Use
nullptrrather than0orNULL - ES.48: Avoid casts
- ES.49: If you must use a cast, use a named cast
- ES.50: Don't cast away
const - ES.55: Avoid the need for range checking
- ES.56: Write
std::move()only when you need to explicitly move an object to another scope - ES.60: Avoid
newanddeleteoutside resource management functions - ES.61: Delete arrays using
delete[]and non-arrays usingdelete - ES.62: Don't compare pointers into different arrays
- ES.63: Don't slice
- ES.64: Use the
T{e}notation for construction - ES.65: Don't dereference an invalid pointer
Statement rules:
- ES.70: Prefer a
switch-statement to anif-statement when there is a choice - ES.71: Prefer a range-
for-statement to afor-statement when there is a choice - ES.72: Prefer a
for-statement to awhile-statement when there is an obvious loop variable - ES.73: Prefer a
while-statement to afor-statement when there is no obvious loop variable - ES.74: Prefer to declare a loop variable in the initializer part of a
for-statement - ES.75: Avoid
do-statements - ES.76: Avoid
goto - ES.77: Minimize the use of
breakandcontinuein loops - ES.78: Always end a non-empty
casewith abreak - ES.79: Use
defaultto handle common cases (only) - ES.84: Don't (try to) declare a local variable with no name
- ES.85: Make empty statements visible
- ES.86: Avoid modifying loop control variables inside the body of raw for-loops
- ES.87: Don't add redundant
==or!=to conditions
Arithmetic rules:
- 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.103: Don't overflow
- ES.104: Don't underflow
- ES.105: Don't divide by zero
- ES.106: Don't try to avoid negative values by using
unsigned - ES.107: Don't use
unsignedfor subscripts, prefergsl::index
??? should this section be in the main guide???
This section contains rules for people who need high performance or low-latency. That is, these are rules that relate to how to use as little time and as few resources as possible to achieve a task in a predictably short time. The rules in this section are more restrictive and intrusive than what is needed for many (most) applications. Do not blindly try to follow them in general code: achieving the goals of low latency requires extra work.
Performance rule summary:
- Per.1: Don't optimize without reason
- Per.2: Don't optimize prematurely
- Per.3: Don't optimize something that's not performance critical
- Per.4: Don't assume that complicated code is necessarily faster than simple code
- Per.5: Don't assume that low-level code is necessarily faster than high-level code
- Per.6: Don't make claims about performance without measurements
- 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.12: Eliminate redundant aliases
- Per.13: Eliminate redundant indirections
- Per.14: Minimize the number of allocations and deallocations
- Per.15: Do not allocate on a critical branch
- Per.16: Use compact data structures
- Per.17: Declare the most used member of a time-critical struct first
- Per.18: Space is time
- Per.19: Access memory predictably
- Per.30: Avoid context switches on the critical path
We often want our computers to do many tasks at the same time (or at least make them appear to do them at the same time). The reasons for doing so varies (e.g., wanting to wait for many events using only a single processor, processing many data streams simultaneously, or utilizing many hardware facilities) and so does the basic facilities for expressing concurrency and parallelism. Here, we articulate a few general principles and rules for using the ISO standard C++ facilities for expressing basic concurrency and parallelism.
The core machine support for concurrent and parallel programming is the thread.
Threads allow you to run multiple instances of your program independently, while sharing
the same memory. Concurrent programming is tricky for many reasons, most
importantly that it is undefined behavior to read data in one thread after it
was written by another thread, if there is no proper synchronization between
those threads. Making existing single-threaded code execute concurrently can be
as trivial as adding std::async or std::thread strategically, or it can
necessitate a full rewrite, depending on whether the original code was written
in a thread-friendly way.
The concurrency/parallelism rules in this document are designed with three goals in mind:
- To help you write code that is amenable to being used in a threaded environment
- To show clean, safe ways to use the threading primitives offered by the standard library
- To offer guidance on what to do when concurrency and parallelism aren't giving you the performance gains you need
It is also important to note that concurrency in C++ is an unfinished story. C++11 introduced many core concurrency primitives, C++14 improved on them, and it seems that there is much interest in making the writing of concurrent programs in C++ even easier. We expect some of the library-related guidance here to change significantly over time.
This section needs a lot of work (obviously). Please note that we start with rules for relative non-experts. Real experts must wait a bit; contributions are welcome, but please think about the majority of programmers who are struggling to get their concurrent programs correct and performant.
Concurrency and parallelism rule summary:
- 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
volatilefor synchronization - CP.9: Whenever feasible use tools to validate your concurrent code
See also:
- CP.con: Concurrency
- CP.par: Parallelism
- CP.mess: Message passing
- CP.vec: Vectorization
- CP.free: Lock-free programming
- CP.etc: Etc. concurrency rules
Error handling involves:
- Detecting an error
- Transmitting information about an error to some handler code
- Preserve the state of a program in a valid state
- Avoid resource leaks
It is not possible to recover from all errors. If recovery from an error is not possible, it is important to quickly "get out" in a well-defined way. A strategy for error handling must be simple, or it becomes a source of even worse errors. Untested and rarely executed error-handling code is itself the source of many bugs.
The rules are designed to help avoid several kinds of errors:
- Type violations (e.g., misuse of
unions and casts) - Resource leaks (including memory leaks)
- Bounds errors
- Lifetime errors (e.g., accessing an object after is has been
deleted) - Complexity errors (logical errors made likely by overly complex expression of ideas)
- Interface errors (e.g., an unexpected value is passed through an interface)
Error-handling rule summary:
-
E.2: Throw an exception to signal that a function can't perform its assigned task
-
E.5: Let a constructor establish an invariant, and throw if it cannot
-
E.12: Use
noexceptwhen exiting a function because of athrowis impossible or unacceptable -
E.14: Use purpose-designed user-defined types as exceptions (not built-in types)
-
E.19: Use a
final_actionobject to express cleanup if no suitable resource handle is available -
E.25: If you can't throw exceptions, simulate RAII for resource management
-
E.27: If you can't throw exceptions, use error codes systematically
-
E.28: Avoid error handling based on global state (e.g.
errno)
You can't have a race condition on a constant. It is easier to reason about a program when many of the objects cannot change their values. Interfaces that promises "no change" of objects passed as arguments greatly increase readability.
Constant 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
consts - Con.4: Use
constto define objects with values that do not change after construction - Con.5: Use
constexprfor values that can be computed at compile time
Generic programming is programming using types and algorithms parameterized by types, values, and algorithms.
In C++, generic programming is supported by the template language mechanisms.
Arguments to generic functions are characterized by sets of requirements on the argument types and values involved. In C++, these requirements are expressed by compile-time predicates called concepts.
Templates can also be used for meta-programming; that is, programs that compose code at compile time.
A central notion in generic programming is "concepts"; that is, requirements on template arguments presented as compile-time predicates. "Concepts" are defined in an ISO Technical specification: concepts. A draft of a set of standard-library concepts can be found in another ISO TS: ranges Concepts are supported in GCC 6.1 and later. Consequently, we comment out uses of concepts in examples; that is, we use them as formalized comments only. If you use GCC 6.1 or later, you can uncomment them.
Template use rule summary:
- 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.4: Use templates to express syntax tree manipulation
- T.5: Combine generic and OO techniques to amplify their strengths, not their costs
Concept use rule summary:
- T.10: Specify concepts for all template arguments
- T.11: Whenever possible use standard concepts
- T.12: Prefer concept names over
autofor local variables - T.13: Prefer the shorthand notation for simple, single-type argument concepts
- ???
Concept definition rule summary:
- T.20: Avoid "concepts" without meaningful semantics
- T.21: Require a complete set of operations for a concept
- T.22: Specify axioms for concepts
- T.23: Differentiate a refined concept from its more general case by adding new use patterns
- T.24: Use tag classes or traits to differentiate concepts that differ only in semantics
- T.25: Avoid complementary constraints
- T.26: Prefer to define concepts in terms of use-patterns rather than simple syntax
- T.30: Use concept negation (
!C<T>) sparingly to express a minor difference - T.31: Use concept disjunction (
C1<T> || C2<T>) sparingly to express alternatives - ???
Template interface rule summary:
- T.40: Use function objects to pass operations to algorithms
- T.41: Require only essential properties in a template's concepts
- T.42: Use template aliases to simplify notation and hide implementation details
- T.43: Prefer
usingovertypedeffor defining aliases - T.44: Use function templates to deduce class template argument types (where feasible)
- T.46: Require template arguments to be at least
RegularorSemiRegular - 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.49: Where possible, avoid type-erasure
Template definition rule summary:
- T.60: Minimize a template's context dependencies
- T.61: Do not over-parameterize members (SCARY)
- T.62: Place non-dependent class template members in a non-templated base class
- T.64: Use specialization to provide alternative implementations of class templates
- T.65: Use tag dispatch to provide alternative implementations of functions
- T.67: Use specialization to provide alternative implementations for irregular types
- T.68: Use
{}rather than()within templates to avoid ambiguities - T.69: Inside a template, don't make an unqualified nonmember function call unless you intend it to be a customization point
Template and hierarchy rule summary:
- T.80: Do not naively templatize a class hierarchy
- T.81: Do not mix hierarchies and arrays // ??? somewhere in "hierarchies"
- T.82: Linearize a hierarchy when virtual functions are undesirable
- T.83: Do not declare a member function template virtual
- T.84: Use a non-template core implementation to provide an ABI-stable interface
- T.??: ????
Variadic template rule summary:
- T.100: Use variadic templates when you need a function that takes a variable number of arguments of a variety of types
- T.101: ??? How to pass arguments to a variadic template ???
- T.102: ??? How to process arguments to a variadic template ???
- T.103: Don't use variadic templates for homogeneous argument lists
- T.??: ????
Metaprogramming rule summary:
- T.120: Use template metaprogramming only when you really need to
- T.121: Use template metaprogramming primarily to emulate concepts
- T.122: Use templates (usually template aliases) to compute types at compile time
- T.123: Use
constexprfunctions to compute values at compile time - T.124: Prefer to use standard-library TMP facilities
- T.125: If you need to go beyond the standard-library TMP facilities, use an existing library
- T.??: ????
Other template rules summary:
- 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.142: Use template variables to simplify notation
- T.143: Don't write unintentionally nongeneric code
- T.144: Don't specialize function templates
- T.150: Check that a class matches a concept using
static_assert - T.??: ????
C and C++ are closely related languages. They both originate in "Classic C" from 1978 and have evolved in ISO committees since then. Many attempts have been made to keep them compatible, but neither is a subset of the other.
C rule summary:
- 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
Distinguish between declarations (used as interfaces) and definitions (used as implementations). Use header files to represent interfaces and to emphasize logical structure.
Source file rule summary:
-
SF.2: A
.hfile may not contain object definitions or non-inline function definitions -
SF.3: Use
.hfiles for all declarations used in multiple source files -
SF.5: A
.cppfile must include the.hfile(s) that defines its interface -
SF.7: Don't write
using namespaceat global scope in a header file -
SF.21: Don't use an unnamed (anonymous) namespace in a header
-
SF.22: Use an unnamed (anonymous) namespace for all internal/nonexported entities
Using only the bare language, every task is tedious (in any language). Using a suitable library any task can be reasonably simple.
The standard library has steadily grown over the years. Its description in the standard is now larger than that of the language features. So, it is likely that this library section of the guidelines will eventually grow in size to equal or exceed all the rest.
<< ??? We need another level of rule numbering ??? >>
C++ Standard Library component summary:
- SL.con: Containers
- SL.str: String
- SL.io: Iostream
- SL.regex: Regex
- SL.chrono: Time
- SL.C: The C Standard Library
Standard-library rule summary:
- SL.1: Use libraries wherever possible
- SL.2: Prefer the standard library to other libraries
- SL.3: Do not add non-standard entities to namespace
std - SL.4: Use the standard library in a type-safe manner
- ???
This section contains ideas about higher-level architectural ideas and libraries.
Architectural rule summary:
- A.1: Separate stable from less stable part of code
- A.2: Express potentially reusable parts as a library
- A.4: There should be no cycles among libraries
- ???
- ???
- ???
- ???
- ???
- ???
This section contains rules and guidelines that are popular somewhere, but that we deliberately don't recommend. We know full well that there have been times and places where these rules made sense, and we have used them ourselves at times. However, in the context of the styles of programming we recommend and support with the guidelines, these "non-rules" would do harm.
Even today, there can be contexts where the rules make sense. For example, lack of suitable tool support can make exceptions unsuitable in hard-real-time systems, but please don't blindly trust "common wisdom" (e.g., unsupported statements about "efficiency"); such "wisdom" may be based on decades-old information or experienced from languages with very different properties than C++ (e.g., C or Java).
The positive arguments for alternatives to these non-rules are listed in the rules offered as "Alternatives".
Non-rule summary:
- NR.1: Don't: All declarations should be at the top of a function
- NR.2: Don't: Have only a single
return-statement in a function - NR.3: Don't: Don't use exceptions
- NR.4: Don't: Place each class declaration in its own source file
- NR.5: Don't: Don't do substantive work in a constructor; instead 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 - ???
Many coding standards, rules, and guidelines have been written for C++, and especially for specialized uses of C++. Many
- focus on lower-level issues, such as the spelling of identifiers
- are written by C++ novices
- see "stopping programmers from doing unusual things" as their primary aim
- aim at portability across many compilers (some 10 years old)
- are written to preserve decades old code bases
- aim at a single application domain
- are downright counterproductive
- are ignored (must be ignored by programmers to get their work done well)
A bad coding standard is worse than no coding standard. However an appropriate set of guidelines are much better than no standards: "Form is liberating."
Why can't we just have a language that allows all we want and disallows all we don't want ("a perfect language")? Fundamentally, because affordable languages (and their tool chains) also serve people with needs that differ from yours and serve more needs than you have today. Also, your needs change over time and a general-purpose language is needed to allow you to adapt. A language that is ideal for today would be overly restrictive tomorrow.
Coding guidelines adapt the use of a language to specific needs. Thus, there cannot be a single coding style for everybody. We expect different organizations to provide additions, typically with more restrictions and firmer style rules.
Reference sections:
- RF.rules: Coding rules
- RF.books: Books with coding guidelines
- RF.C++: C++ Programming (C++11/C++14)
- RF.web: Websites
- RS.video: Videos about "modern C++"
- RF.man: Manuals
- RF.core: Core Guidelines materials
Ideally, we would follow all of the guidelines. That would give the cleanest, most regular, least error-prone, and often the fastest code. Unfortunately, that is usually impossible because we have to fit our code into large code bases and use existing libraries. Often, such code has been written over decades and does not follow these guidelines. We must aim for gradual adoption.
Whatever strategy for gradual adoption we adopt, we need to be able to apply sets of related guidelines to address some set of problems first and leave the rest until later. A similar idea of "related guidelines" becomes important when some, but not all, guidelines are considered relevant to a code base or if a set of specialized guidelines is to be applied for a specialized application area. We call such a set of related guidelines a "profile". We aim for such a set of guidelines to be coherent so that they together help us reach a specific goal, such as "absence of range errors" or "static type safety." Each profile is designed to eliminate a class of errors. Enforcement of "random" rules in isolation is more likely to be disruptive to a code base than delivering a definite improvement.
A "profile" is a set of deterministic and portably enforceable subset rules (i.e., restrictions) that are designed to achieve a specific guarantee. "Deterministic" means they require only local analysis and could be implemented in a compiler (though they don't need to be). "Portably enforceable" means they are like language rules, so programmers can count on different enforcement tools giving the same answer for the same code.
Code written to be warning-free using such a language profile is considered to conform to the profile. Conforming code is considered to be safe by construction with regard to the safety properties targeted by that profile. Conforming code will not be the root cause of errors for that property, although such errors may be introduced into a program by other code, libraries or the external environment. A profile may also introduce additional library types to ease conformance and encourage correct code.
Profiles summary:
In the future, we expect to define many more profiles and add more checks to existing profiles. Candidates include:
- narrowing arithmetic promotions/conversions (likely part of a separate safe-arithmetic profile)
- arithmetic cast from negative floating point to unsigned integral type (ditto)
- selected undefined behavior: Start with Gabriel Dos Reis's UB list developed for the WG21 study group
- selected unspecified behavior: Addressing portability concerns.
constviolations: Mostly done by compilers already, but we can catch inappropriate casting and underuse ofconst.
Enabling a profile is implementation defined; typically, it is set in the analysis tool used.
To suppress enforcement of a profile check, place a suppress annotation on a language contract. For example:
[[suppress(bounds)]] char* raw_find(char* p, int n, char x) // find x in p[0]..p[n - 1]
{
// ...
}
Now raw_find() can scramble memory to its heart's content.
Obviously, suppression should be very rare.
The GSL is a small library of facilities designed to support this set of guidelines. Without these facilities, the guidelines would have to be far more restrictive on language details.
The Core Guidelines support library is defined in namespace gsl and the names may be aliases for standard library or other well-known library names. Using the (compile-time) indirection through the gsl namespace allows for experimentation and for local variants of the support facilities.
The GSL is header only, and can be found at GSL: Guidelines support library. The support library facilities are designed to be extremely lightweight (zero-overhead) so that they impose no overhead compared to using conventional alternatives. Where desirable, they can be "instrumented" with additional functionality (e.g., checks) for tasks such as debugging.
These Guidelines assume a variant type, but this is not currently in GSL.
Eventually, use the one voted into C++17.
Summary of GSL components:
We plan for a "ISO C++ standard style" semi-formal specification of the GSL.
We rely on the ISO C++ Standard Library and hope for parts of the GSL to be absorbed into the standard library.
Consistent naming and layout are helpful. If for no other reason because it minimizes "my style is better than your style" arguments. However, there are many, many, different styles around and people are passionate about them (pro and con). Also, most real-world projects includes code from many sources, so standardizing on a single style for all code is often impossible. We present a set of rules that you might use if you have no better ideas, but the real aim is consistency, rather than any particular rule set. IDEs and tools can help (as well as hinder).
Naming and layout rules:
- NL.1: Don't say in comments what can be clearly stated in code
- NL.2: State intent in comments
- NL.3: Keep comments crisp
- NL.4: Maintain a consistent indentation style
- NL.5: Avoid encoding type information in names
- NL.7: Make the length of a name roughly proportional to the length of its scope
- NL.8: Use a consistent naming style
- NL.9: Use
ALL_CAPSfor macro names only - NL.10: Prefer
underscore_stylenames - NL.11: Make literals readable
- NL.15: Use spaces sparingly
- NL.16: Use a conventional class member declaration order
- NL.17: Use K&R-derived layout
- NL.18: Use C++-style declarator layout
- NL.19: Avoid names that are easily misread
- NL.20: Don't place two statements on the same line
- NL.21: Declare one name (only) per declaration
- NL.25: Don't use
voidas an argument type - NL.26: Use conventional
constnotation
Most of these rules are aesthetic and programmers hold strong opinions. IDEs also tend to have defaults and a range of alternatives. These rules are suggested defaults to follow unless you have reasons not to.
We have had comments to the effect that naming and layout are so personal and/or arbitrary that we should not try to "legislate" them. We are not "legislating" (see the previous paragraph). However, we have had many requests for a set of naming and layout conventions to use when there are no external constraints.
More specific and detailed rules are easier to enforce.
These rules bear a strong resemblance to the recommendations in the PPP Style Guide written in support of Stroustrup's Programming: Principles and Practice using C++.
This section covers answers to frequently asked questions about these guidelines.
This section lists recommended libraries, and explicitly recommends a few.
??? Suitable for the general guide? I think not ???
Ideally, we follow all rules in all code. Realistically, we have to deal with a lot of old code:
- application code written before the guidelines were formulated or known
- libraries written to older/different standards
- code written under "unusual" constraints
- code that we just haven't gotten around to modernizing
If we have a million lines of new code, the idea of "just changing it all at once" is typically unrealistic. Thus, we need a way of gradually modernizing a code base.
Upgrading older code to modern style can be a daunting task. Often, the old code is both a mess (hard to understand) and working correctly (for the current range of uses). Typically, the original programmer is not around and the test cases incomplete. The fact that the code is a mess dramatically increases the effort needed to make any change and the risk of introducing errors. Often, messy old code runs unnecessarily slowly because it requires outdated compilers and cannot take advantage of modern hardware. In many cases, automated "modernizer"-style tool support would be required for major upgrade efforts.
The purpose of modernizing code is to simplify adding new functionality, to ease maintenance, and to increase performance (throughput or latency), and to better utilize modern hardware. Making code "look pretty" or "follow modern style" are not by themselves reasons for change. There are risks implied by every change and costs (including the cost of lost opportunities) implied by having an outdated code base. The cost reductions must outweigh the risks.
But how?
There is no one approach to modernizing code. How best to do it depends on the code, the pressure for updates, the backgrounds of the developers, and the available tool. Here are some (very general) ideas:
- The ideal is "just upgrade everything." That gives the most benefits for the shortest total time. In most circumstances, it is also impossible.
- We could convert a code base module for module, but any rules that affects interfaces (especially ABIs), such as use
span, cannot be done on a per-module basis. - We could convert code "bottom up" starting with the rules we estimate will give the greatest benefits and/or the least trouble in a given code base.
- We could start by focusing on the interfaces, e.g., make sure that no resources are lost and no pointer is misused. This would be a set of changes across the whole code base, but would most likely have huge benefits. Afterwards, code hidden behind those interfaces can be gradually modernized without affecting other code.
Whichever way you choose, please note that the most advantages come with the highest conformance to the guidelines. The guidelines are not a random set of unrelated rules where you can randomly pick and choose with an expectation of success.
We would dearly love to hear about experience and about tools used. Modernization can be much faster, simpler, and safer when supported with analysis tools and even code transformation tools.
This section contains follow-up material on rules and sets of rules. In particular, here we present further rationale, longer examples, and discussions of alternatives.
This section contains a list of tools that directly support adoption of the C++ Core Guidelines. This list is not intended to be an exhaustive list of tools that are helpful in writing good C++ code. If a tool is designed specifically to support and links to the C++ Core Guidelines it is a candidate for inclusion.
A relatively informal definition of terms used in the guidelines (based of the glossary in Programming: Principles and Practice using C++)
More information on many topics about C++ can be found on the Standard C++ Foundation's site.
- ABI: Application Binary Interface, a specification for a specific hardware platform combined with the operating system. Contrast with API.
- abstract class: a class that cannot be directly used to create objects; often used to define an interface to derived classes. A class is made abstract by having a pure virtual function or only protected constructors.
- abstraction: a description of something that selectively and deliberately ignores (hides) details (e.g., implementation details); selective ignorance.
- address: a value that allows us to find an object in a computer's memory.
- algorithm: a procedure or formula for solving a problem; a finite series of computational steps to produce a result.
- alias: an alternative way of referring to an object; often a name, pointer, or reference.
- API: Application Programming Interface, a set of functions that form the communication between various software components. Contrast with ABI.
- application: a program or a collection of programs that is considered an entity by its users.
- approximation: something (e.g., a value or a design) that is close to the perfect or ideal (value or design). Often an approximation is a result of trade-offs among ideals.
- argument: a value passed to a function or a template, in which it is accessed through a parameter.
- array: a homogeneous sequence of elements, usually numbered, e.g.,
[0:max). - assertion: a statement inserted into a program to state (assert) that something must always be true at this point in the program.
- base class: a class used as the base of a class hierarchy. Typically a base class has one or more virtual functions.
- bit: the basic unit of information in a computer. A bit can have the value 0 or the value 1.
- bug: an error in a program.
- byte: the basic unit of addressing in most computers. Typically, a byte holds 8 bits.
- class: a user-defined type that may contain data members, function members, and member types.
- code: a program or a part of a program; ambiguously used for both source code and object code.
- compiler: a program that turns source code into object code.
- complexity: a hard-to-precisely-define notion or measure of the difficulty of constructing a solution to a problem or of the solution itself. Sometimes complexity is used to (simply) mean an estimate of the number of operations needed to execute an algorithm.
- computation: the execution of some code, usually taking some input and producing some output.
- concept: (1) a notion, and idea; (2) a set of requirements, usually for a template argument.
- concrete class: class for which objects can be created.
- constant: a value that cannot be changed (in a given scope); not mutable.
- constructor: an operation that initializes ("constructs") an object. Typically a constructor establishes an invariant and often acquires resources needed for an object to be used (which are then typically released by a destructor).
- container: an object that holds elements (other objects).
- copy: an operation that makes two object have values that compare equal. See also move.
- correctness: a program or a piece of a program is correct if it meets its specification. Unfortunately, a specification can be incomplete or inconsistent, or can fail to meet users' reasonable expectations. Thus, to produce acceptable code, we sometimes have to do more than just follow the formal specification.
- cost: the expense (e.g., in programmer time, run time, or space) of producing a program or of executing it. Ideally, cost should be a function of complexity.
- customization point: ???
- data: values used in a computation.
- debugging: the act of searching for and removing errors from a program; usually far less systematic than testing.
- declaration: the specification of a name with its type in a program.
- definition: a declaration of an entity that supplies all information necessary to complete a program using the entity. Simplified definition: a declaration that allocates memory.
- derived class: a class derived from one or more base classes.
- design: an overall description of how a piece of software should operate to meet its specification.
- destructor: an operation that is implicitly invoked (called) when an object is destroyed (e.g., at the end of a scope). Often, it releases resources.
- encapsulation: protecting something meant to be private (e.g., implementation details) from unauthorized access.
- error: a mismatch between reasonable expectations of program behavior (often expressed as a requirement or a users' guide) and what a program actually does.
- executable: a program ready to be run (executed) on a computer.
- feature creep: a tendency to add excess functionality to a program "just in case."
- file: a container of permanent information in a computer.
- floating-point number: a computer's approximation of a real number, such as 7.93 and 10.78e-3.
- function: a named unit of code that can be invoked (called) from different parts of a program; a logical unit of computation.
- generic programming: a style of programming focused on the design and efficient implementation of algorithms. A generic algorithm will work for all argument types that meet its requirements. In C++, generic programming typically uses templates.
- global variable: technically, a named object in namespace scope.
- handle: a class that allows access to another through a member pointer or reference. See also resource, copy, move.
- header: a file containing declarations used to share interfaces between parts of a program.
- hiding: the act of preventing a piece of information from being directly seen or accessed. For example, a name from a nested (inner) scope can prevent that same name from an outer (enclosing) scope from being directly used.
- ideal: the perfect version of something we are striving for. Usually we have to make trade-offs and settle for an approximation.
- implementation: (1) the act of writing and testing code; (2) the code that implements a program.
- infinite loop: a loop where the termination condition never becomes true. See iteration.
- infinite recursion: a recursion that doesn't end until the machine runs out of memory to hold the calls. In reality, such recursion is never infinite but is terminated by some hardware error.
- information hiding: the act of separating interface and implementation, thus hiding implementation details not meant for the user's attention and providing an abstraction.
- initialize: giving an object its first (initial) value.
- input: values used by a computation (e.g., function arguments and characters typed on a keyboard).
- integer: a whole number, such as 42 and -99.
- interface: a declaration or a set of declarations specifying how a piece of code (such as a function or a class) can be called.
- invariant: something that must be always true at a given point (or points) of a program; typically used to describe the state (set of values) of an object or the state of a loop before entry into the repeated statement.
- iteration: the act of repeatedly executing a piece of code; see recursion.
- iterator: an object that identifies an element of a sequence.
- ISO: International Organization for Standardization. The C++ language is an ISO standard, ISO/IEC 14882. More information at iso.org.
- library: a collection of types, functions, classes, etc. implementing a set of facilities (abstractions) meant to be potentially used as part of more that one program.
- lifetime: the time from the initialization of an object until it becomes unusable (goes out of scope, is deleted, or the program terminates).
- linker: a program that combines object code files and libraries into an executable program.
- literal: a notation that directly specifies a value, such as 12 specifying the integer value "twelve."
- loop: a piece of code executed repeatedly; in C++, typically a for-statement or a
while-statement. - move: an operation that transfers a value from one object to another leaving behind a value representing "empty." See also copy.
- mutable: changeable; the opposite of immutable, constant, and invariable.
- object: (1) an initialized region of memory of a known type which holds a value of that type; (2) a region of memory.
- object code: output from a compiler intended as input for a linker (for the linker to produce executable code).
- object file: a file containing object code.
- object-oriented programming: (OOP) a style of programming focused on the design and use of classes and class hierarchies.
- operation: something that can perform some action, such as a function and an operator.
- output: values produced by a computation (e.g., a function result or lines of characters written on a screen).
- overflow: producing a value that cannot be stored in its intended target.
- overload: defining two functions or operators with the same name but different argument (operand) types.
- override: defining a function in a derived class with the same name and argument types as a virtual function in the base class, thus making the function callable through the interface defined by the base class.
- owner: an object responsible for releasing a resource.
- paradigm: a somewhat pretentious term for design or programming style; often used with the (erroneous) implication that there exists a paradigm that is superior to all others.
- parameter: a declaration of an explicit input to a function or a template. When called, a function can access the arguments passed through the names of its parameters.
- pointer: (1) a value used to identify a typed object in memory; (2) a variable holding such a value.
- post-condition: a condition that must hold upon exit from a piece of code, such as a function or a loop.
- pre-condition: a condition that must hold upon entry into a piece of code, such as a function or a loop.
- program: code (possibly with associated data) that is sufficiently complete to be executed by a computer.
- programming: the art of expressing solutions to problems as code.
- programming language: a language for expressing programs.
- pseudo code: a description of a computation written in an informal notation rather than a programming language.
- pure virtual function: a virtual function that must be overridden in a derived class.
- RAII: ("Resource Acquisition Is Initialization") a basic technique for resource management based on scopes.
- range: a sequence of values that can be described by a start point and an end point. For example,
[0:5)means the values 0, 1, 2, 3, and 4. - recursion: the act of a function calling itself; see also iteration.
- reference: (1) a value describing the location of a typed value in memory; (2) a variable holding such a value.
- regular expression: a notation for patterns in character strings.
- regular: a type that behaves similarly to built-in types like
intand can be compared with==. In particular, an object of a regular type can be copied and the result of a copy is a separate object that compares equal to the original. See also semiregular type. - requirement: (1) a description of the desired behavior of a program or part of a program; (2) a description of the assumptions a function or template makes of its arguments.
- resource: something that is acquired and must later be released, such as a file handle, a lock, or memory. See also handle, owner.
- rounding: conversion of a value to the mathematically nearest value of a less precise type.
- RTTI: Run-Time Type Information. ???
- scope: the region of program text (source code) in which a name can be referred to.
- semiregular: a type that behaves roughly like an built-in type like
int, but possibly without a==operator. See also regular type. - sequence: elements that can be visited in a linear order.
- software: a collection of pieces of code and associated data; often used interchangeably with program.
- source code: code as produced by a programmer and (in principle) readable by other programmers.
- source file: a file containing source code.
- specification: a description of what a piece of code should do.
- standard: an officially agreed upon definition of something, such as a programming language.
- state: a set of values.
- STL: the containers, iterators, and algorithms part of the standard library.
- string: a sequence of characters.
- style: a set of techniques for programming leading to a consistent use of language features; sometimes used in a very restricted sense to refer just to low-level rules for naming and appearance of code.
- subtype: derived type; a type that has all the properties of a type and possibly more.
- supertype: base type; a type that has a subset of the properties of a type.
- system: (1) a program or a set of programs for performing a task on a computer; (2) a shorthand for "operating system", that is, the fundamental execution environment and tools for a computer.
- TS: Technical Specification, A Technical Specification addresses work still under technical development, or where it is believed that there will be a future, but not immediate, possibility of agreement on an International Standard. A Technical Specification is published for immediate use, but it also provides a means to obtain feedback. The aim is that it will eventually be transformed and republished as an International Standard.
- template: a class or a function parameterized by one or more types or (compile-time) values; the basic C++ language construct supporting generic programming.
- testing: a systematic search for errors in a program.
- trade-off: the result of balancing several design and implementation criteria.
- truncation: loss of information in a conversion from a type into another that cannot exactly represent the value to be converted.
- type: something that defines a set of possible values and a set of operations for an object.
- uninitialized: the (undefined) state of an object before it is initialized.
- unit: (1) a standard measure that gives meaning to a value (e.g., km for a distance); (2) a distinguished (e.g., named) part of a larger whole.
- use case: a specific (typically simple) use of a program meant to test its functionality and demonstrate its purpose.
- value: a set of bits in memory interpreted according to a type.
- variable: a named object of a given type; contains a value unless uninitialized.
- virtual function: a member function that can be overridden in a derived class.
- word: a basic unit of memory in a computer, often the unit used to hold an integer.
This is our to-do list. Eventually, the entries will become rules or parts of rules. Alternatively, we will decide that no change is needed and delete the entry.
-
No long-distance friendship
-
Should physical design (what's in a file) and large-scale design (libraries, groups of libraries) be addressed?
-
Namespaces
-
Avoid using directives in the global scope (except for std, and other "fundamental" namespaces (e.g. experimental))
-
How granular should namespaces be? All classes/functions designed to work together and released together (as defined in Sutter/Alexandrescu) or something narrower or wider?
-
Should there be inline namespaces (à la
std::literals::*_literals)? -
Avoid implicit conversions
-
Const member functions should be thread safe ... aka, but I don't really change the variable, just assign it a value the first time it's called ... argh
-
Always initialize variables, use initialization lists for member variables.
-
Anyone writing a public interface which takes or returns
void*should have their toes set on fire. That one has been a personal favorite of mine for a number of years. :) -
Use
const-ness wherever possible: member functions, variables and (yippee)const_iterators -
Use
auto -
(size)vs.{initializers}vs.{Extent{size}} -
Don't overabstract
-
Never pass a pointer down the call stack
-
falling through a function bottom
-
Should there be guidelines to choose between polymorphisms? YES. classic (virtual functions, reference semantics) vs. Sean Parent style (value semantics, type-erased, kind of like
std::function) vs. CRTP/static? YES Perhaps even vs. tag dispatch? -
should virtual calls be banned from ctors/dtors in your guidelines? YES. A lot of people ban them, even though I think it's a big strength of C++ that they are ??? -preserving (D disappointed me so much when it went the Java way). WHAT WOULD BE A GOOD EXAMPLE?
-
Speaking of lambdas, what would weigh in on the decision between lambdas and (local?) classes in algorithm calls and other callback scenarios?
-
And speaking of
std::bind, Stephen T. Lavavej criticizes it so much I'm starting to wonder if it is indeed going to fade away in future. Should lambdas be recommended instead? -
What to do with leaks out of temporaries? :
p = (s1 + s2).c_str(); -
pointer/iterator invalidation leading to dangling pointers:
void bad() { int* p = new int[700]; int* q = &p[7]; delete p; vector<int> v(700); int* q2 = &v[7]; v.resize(900); // ... use q and q2 ... } -
LSP
-
private inheritance vs/and membership
-
avoid static class members variables (race conditions, almost-global variables)
-
Use RAII lock guards (
lock_guard,unique_lock,shared_lock), never callmutex.lockandmutex.unlockdirectly (RAII) -
Prefer non-recursive locks (often used to work around bad reasoning, overhead)
-
Join your threads! (because of
std::terminatein destructor if not joined or detached ... is there a good reason to detach threads?) -- ??? could support library provide a RAII wrapper forstd::thread? -
If two or more mutexes must be acquired at the same time, use
std::lock(or another deadlock avoidance algorithm?) -
When using a
condition_variable, always protect the condition by a mutex (atomic bool whose value is set outside of the mutex is wrong!), and use the same mutex for the condition variable itself. -
Never use
atomic_compare_exchange_strongwithstd::atomic<user-defined-struct>(differences in padding matter, whilecompare_exchange_weakin a loop converges to stable padding) -
individual
shared_futureobjects are not thread-safe: two threads cannot wait on the sameshared_futureobject (they can wait on copies of ashared_futurethat refer to the same shared state) -
individual
shared_ptrobjects are not thread-safe: different threads can call non-constmember functions on differentshared_ptrs that refer to the same shared object, but one thread cannot call a non-constmember function of ashared_ptrobject while another thread accesses that sameshared_ptrobject (if you need that, consideratomic_shared_ptrinstead) -
rules for arithmetic
- [Abrahams01]: D. Abrahams. Exception-Safety in Generic Components.
- [Alexandrescu01]: A. Alexandrescu. Modern C++ Design (Addison-Wesley, 2001).
- [C++03]: ISO/IEC 14882:2003(E), Programming Languages — C++ (updated ISO and ANSI C++ Standard including the contents of (C++98) plus errata corrections).
- [C++CS]: ???
- [Cargill92]: T. Cargill. C++ Programming Style (Addison-Wesley, 1992).
- [Cline99]: M. Cline, G. Lomow, and M. Girou. C++ FAQs (2ndEdition) (Addison-Wesley, 1999).
- [Dewhurst03]: S. Dewhurst. C++ Gotchas (Addison-Wesley, 2003).
- [Henricson97]: M. Henricson and E. Nyquist. Industrial Strength C++ (Prentice Hall, 1997).
- [Koenig97]: A. Koenig and B. Moo. Ruminations on C++ (Addison-Wesley, 1997).
- [Lakos96]: J. Lakos. Large-Scale C++ Software Design (Addison-Wesley, 1996).
- [Meyers96]: S. Meyers. More Effective C++ (Addison-Wesley, 1996).
- [Meyers97]: S. Meyers. Effective C++ (2nd Edition) (Addison-Wesley, 1997).
- [Meyers15]: S. Meyers. Effective Modern C++ (O'Reilly, 2015).
- [Murray93]: R. Murray. C++ Strategies and Tactics (Addison-Wesley, 1993).
- [Stroustrup94]: B. Stroustrup. The Design and Evolution of C++ (Addison-Wesley, 1994).
- [Stroustrup00]: B. Stroustrup. The C++ Programming Language (Special 3rdEdition) (Addison-Wesley, 2000).
- [Stroustrup05]: B. Stroustrup. A rationale for semantically enhanced library languages.
- [Stroustrup13]: B. Stroustrup. The C++ Programming Language (4th Edition). Addison Wesley 2013.
- [Stroustrup14]: B. Stroustrup. A Tour of C++. Addison Wesley 2014.
- [Stroustrup15]: B. Stroustrup, Herb Sutter, and G. Dos Reis: A brief introduction to C++'s model for type- and resource-safety.
- [SuttHysl04b]: H. Sutter and J. Hyslop. "Collecting Shared Objects" (C/C++ Users Journal, 22(8), August 2004).
- [SuttAlex05]: H. Sutter and A. Alexandrescu. C++ Coding Standards. Addison-Wesley 2005.
- [Sutter00]: H. Sutter. Exceptional C++ (Addison-Wesley, 2000).
- [Sutter02]: H. Sutter. More Exceptional C++ (Addison-Wesley, 2002).
- [Sutter04]: H. Sutter. Exceptional C++ Style (Addison-Wesley, 2004).
- [Taligent94]: Taligent's Guide to Designing Programs (Addison-Wesley, 1994).