Skip to content

Latest commit

 

History

History
1383 lines (1140 loc) · 94.8 KB

File metadata and controls

1383 lines (1140 loc) · 94.8 KB

C++ Core Guidelines

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:

Supporting sections:

You can sample rules for specific language features:

You can look at design concepts used to express the rules:

  • assertion: ???
  • error: ???
  • exception: exception guarantee (???)
  • failure: ???
  • invariant: ???
  • leak: ???
  • library: ???
  • precondition: ???
  • postcondition: ???
  • resource: ???

Abstract

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.

In: Introduction

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:

P: Philosophy

The rules in this section are very general.

Philosophy rules summary:

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.

I: Interfaces

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:

See also:

F: Functions

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:

Parameter passing expression rules:

Parameter passing semantic rules:

Value return semantic rules:

Other function rules:

Functions have strong similarities to lambdas and function objects.

See also: C.lambdas: Function objects and lambdas

C: Classes and class hierarchies

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:

Subsections:

Enum: Enumerations

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:

R: Resource management

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.

ES: Expressions and statements

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:

Declaration rules:

Expression rules:

Statement rules:

Arithmetic rules:

Per: Performance

??? 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:

CP: Concurrency and parallelism

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:

See also:

E: Error handling

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:

Con: Constants and immutability

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:

T: Templates and generic programming

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:

Concept use rule summary:

Concept definition rule summary:

Template interface rule summary:

Template definition rule summary:

Template and hierarchy rule summary:

Variadic template rule summary:

Metaprogramming rule summary:

Other template rules summary:

CPL: C-style programming

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:

SF: Source files

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:

SL: The Standard Library

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:

Standard-library rule summary:

A: Architectural ideas

This section contains ideas about higher-level architectural ideas and libraries.

Architectural rule summary:

NR: Non-Rules and myths

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:

RF: References

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:

Pro: Profiles

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.
  • const violations: Mostly done by compilers already, but we can catch inappropriate casting and underuse of const.

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.

GSL: Guidelines support library

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.

NL: Naming and layout rules

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:

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++.

FAQ: Answers to frequently asked questions

This section covers answers to frequently asked questions about these guidelines.

Appendix A: Libraries

This section lists recommended libraries, and explicitly recommends a few.

??? Suitable for the general guide? I think not ???

Appendix B: Modernizing code

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.

Appendix C: Discussion

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.

Appendix D: Supporting tools

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.

Glossary

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 int and 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.

To-do: Unclassified proto-rules

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 call mutex.lock and mutex.unlock directly (RAII)

  • Prefer non-recursive locks (often used to work around bad reasoning, overhead)

  • Join your threads! (because of std::terminate in destructor if not joined or detached ... is there a good reason to detach threads?) -- ??? could support library provide a RAII wrapper for std::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_strong with std::atomic<user-defined-struct> (differences in padding matter, while compare_exchange_weak in a loop converges to stable padding)

  • individual shared_future objects are not thread-safe: two threads cannot wait on the same shared_future object (they can wait on copies of a shared_future that refer to the same shared state)

  • individual shared_ptr objects are not thread-safe: different threads can call non-const member functions on different shared_ptrs that refer to the same shared object, but one thread cannot call a non-const member function of a shared_ptr object while another thread accesses that same shared_ptr object (if you need that, consider atomic_shared_ptr instead)

  • rules for arithmetic

Bibliography

  • [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).