Skip to content

Specification Style Guidelines

Thomas Köppe edited this page Jul 12, 2024 · 104 revisions

Standards Wording Idioms

This is a list of some common idioms in the standard that should be used to specify certain cases, and corresponding anti-idioms that should not be used.

When you are writing proposals for changes to the C++ standard, please try to follow these idioms. If you notice an anti-idiom in the current C++ standards draft, please submit an editorial issue.

Case Idiom(s) Anti-idiom(s) Example
Normative requirement (implementation)
Use this idiom when specifying something we require a conforming implementation to do.
  • The behavior of the program is […]
  • The implementation shall […] (only use this form when it is clear we are talking about the implementation)
  • […] must […]
  • an expression shall not have reference type

"A conforming implementation executing a well-formed program shall produce the same observable behavior as one of the possible executions of the corresponding instance of the abstract machine"
Normative requirement (program)
Use this idiom when specifying something we require a well-formed program to do, and equivalently, for something whose converse we require a conforming implementation to diagnose.
  • A prvalue shall have a complete type […]
  • The -> operator shall not be […]
  • […] must […]
  • The program is ill-formed if […] (OK, but not preferred)

"A d-char-sequence shall consist of at most 16 characters"
Normative encouragement
Use this idiom when specifying something we would prefer, but do not require, implementations to do.
  • Implementations should […]
  • Implementations are encouraged, but not required, to […]
"Implementations should ensure that all unblocked threads eventually make progress"
Complicated conditional cases
Use this idiom when you are enumerating a set of possibilities, and stating requirements under those possibilities.
  • If […], then […].
    If […], then […].

    Otherwise […]
  • […], except when […], then […], or when […], then […]
"If E1 is an lvalue, then E1.E2 is an lvalue; if E1 is an xvalue, then E1.E2 is an xvalue; otherwise, it is a prvalue."

Rephrasing ideas to avoid forbidden words

  • and might therefore be defined as deleted -> which can result in them being defined as deleted
  • might -> it is possible that/for

Missing idioms

  • Variations on "ill-formed" (particularly, "no diagnostic is required" forms)

Describing function calls

(Decided in #2150.)

  • Do not use trailing parentheses to mean simply "this name is a function". You may use trailing parentheses when referring to an overloaded function to specify which overload you mean, but if this comes up we should discuss a better way of naming it, such as "the blah(int) overload".

  • When we say "Calls blah", blah should be an expression that results in a function call. Eg, "calls operator new(size)" or "calls `current_exception() and stores the returned value".

  • As an alternative, you may use "Calls blah with foo", and in that case blah should be a function.

  • The form "Calls to the function blah do X, Y, Z" is also OK, and blah should denote a function not an expression. (Per the first bullet, there should be no parentheses unless the function is overloaded.)

Labels

All sections, tables, and figures have a visible label. For tables and figures, a "tab:" or "fig:" prefix is applied implicitly by the corresponding higher-level environments as well as by \tref and \fref, respectively. Restrict all labels to lowercase letters and digits with dots as separators (see #1498). Do not use underscores (they look ugly; see #2609) or hyphens and abbreviate reasonably. Overly long labels are discouraged. See below for specific recommendations for the library clauses.

Use \ref to refer to a section (renders as "Clause X" or an unadorned subclause number), or \iref with automatic surrounding parentheses and discouraging a line break just before the opening parenthesis.

For some defined terms (e.g. "odr-use"), we customarily add a cross-reference after a use of the term. In order to make that cross-reference stable against section moves of the definition of the term, create a \label{term.this.name} at the start of the sentence defining the term, and use \ref or \iref referencing term.this.name`.

For tables and figures, the following rules apply (decided in #2674):

  • If a subclause consists predominantly of a table, use the same label as the subclause label (note implicit tab: prefix). Example: tab:lex.key, tab:lex.digraph.
  • For old-style concepts tables, just use the flat name as-is, prefixed by cpp17.. Example: tab:cpp17.copyconstructible.
  • Otherwise, use at least some parts in common with the subclause label. Example: tab:temp.fold.empty.

Library conventions

You can find a description of the conventions followed by the library clauses in the standard in the standard itself, in the section labeled Method of description (informative) [description] (section 17.5 in C++11).

Library-wide requirements are specified in the standard under Library-wide requirements [requirements] (section 17.6 in C++11). This section describes the type requirements (e.g. EqualityComparable and DefaultConstructible) as well as broad rules for library implementation and usage.

Lexical conventions

Floating-point literals should have a decimal point and at least one digit on each side of the decimal point (for example, 3.0 not 3, 0.1 not .1, 1.0e3 not 1e3).

Requirements expressed by concepts

  • Use "T meets the CppXXX requirements" for "old" concepts.
  • Use "T satisfies Concept" to mean purely syntactic conformance (which is the proper core language meaning of "satisfy").
  • Use "T models Concept" to mean that T must both satisfy Concept and must further meet the semantic requirements. (see #2176)

Subclause headings

Descriptions for library entities should be grouped into reasonably-sized subclauses. Avoid multiple consecutive subclauses that each describe only one function. A rule of thumb is 1-3 subclause headings per library description page. (See below for synopses.)

Do not repeat parts of subclause headings in nested headings (decided in #1242). Good: "Class template tuple" / "Constructors". Bad: "Class template vector" / "vector capacity".

Prefer prose text over C++ tokens in subclause headings. Good: "Concatenation". Bad: "operator+".

The following subclause headings and label parts are good:

  • Constructors [*.cons] (not: Construction; the alternative "*.ctor" label has also been used historically)
  • Deduction guides [*.deduct] [decision required]
  • Assignment [*.assign]
  • Operations [*.ops]
  • Member functions [*.members]
  • Observers [*.obs]
  • Modifiers [*.mod]
  • Navigation [*.nav] (for iterators)
  • Equality operators [*.eq]
  • Relational operators [*.rel] (includes three-way comparison) (note: neither == nor != is a relational operator)
  • Comparison [*.cmp] (includes equality, relational, and three-way comparison operators)

Note: Since != is synthesized based on == and relational operators are synthesized based on <=>, there is rarely a need for separate subclauses for equality, relational, and three-way operators. The recommendation is to have a unified "Comparison" subclause.

Inline definitions in synopses

A synopsis should only show declarations, not definitions. In general, semantic definitions go into separate itemdecl / itemdescr pairs. Exceptions are:

  • typedefs
  • = default and = delete
  • delegating constructors
  • hidden friends (but see discussion in #3110)

At any point, \seebelow can be used to refer to an out-of-line description (e.g. for return types, exception-specifications, or typedefs).

Writing "Let" in a function description

When describing the semantics of a library function, it's sometimes useful to introduce a new name for something, typically to shorten it. In that case, write a paragraph ahead of the Effects, Returns, and similar paragraphs to do so.

For example:

  1. Let T be decltype(foo).
  2. Returns: T{0}.
  3. Mandates: T meets the Cpp17CopyConstructible requirements.

Do not put a "Let" statement in the Remarks paragraph of a function description.

Writing Effects: in a function description

Effects: elements are used in the library to describe functions. They can be described in words, or in code preceded by an introductory phrase of some kind. The introductory phase "Equivalent to" is normative and has special meaning, as is described in [structure.specifications]/4. When code is used to describe an effects element, a colon (:) should follow the introductory phrase if the code is a statement or a code block, whereas expressions should end with a period (.). See the general rules on codeblocks. For "Equivalent to", a plain expression should appear only if it is of type void; for value-returning functions, use return expression; (see below).

For example:

  1. Effects: The function works in this way.

  2. Effects: Equivalent to expression.

  3. Effects: Equivalent to:

     statement1;
     statement2;
    

Note that return 42 is not an expression (it's not an anything -- the return grammar production has a trailing ;), so this is wrong:

Effects: Equivalent to return 42.

and these are acceptable:

Effects: Equivalent to: return 42;

Effects: Equivalent to:

    return 42;

where 42 is explained further in trailing text.

Writing Returns: in a function description

Returns: elements are similar to Effects: elements. They should always end in a full stop. They may be just a single expression, or phrase or sentence. If the text starts with normal text, it should be capitalized. Very long expressions may use a codeblock (without a full stop at the end). See the general rules on codeblocks.

For example:

  1. Returns: !(a == b).

  2. Returns: true if foo; false otherwise.

  3. Returns: A copy of the object described in this sentence.

  4. Returns:

     a_very_long_expression()
    

Other library elements

Do not use the Requires: element; the Expects: and Throws: elements should be used instead.

Specifying implicitly generated special member functions

When you are specifying a class containing implicitly generated member functions, do not provide a detailed specification of these, as their behavior is implied by the language rules. Instead, list the member functions using the = default mechanism in the class synopsis only. For example:

class my_class {
  // ...
public:
  my_class() noexcept = default;
  my_class(const my_class&) = default;
  my_class& operator=(const my_class&) = default;
  my_class(my_class&&) noexcept = default;
  my_class& operator=(my_class&&) noexcept = default;
  ~my_class() noexcept = default;
  // ...
};

Formatting declarations and definitions

These rules apply within library clauses. Within core clauses, we intentionally use a variety of different styles to emphasize that the core language does not enforce any particular style.

  • Use two-space indentation when breaking a declaration after a template-head or return type; braced (class, enum, function) definitions do not indent after the template-head; indent to the active < or ( when breaking within a template or function parameter list and to after the requires keyword when breaking within a requires-clause. (#1754)
  • Template parameter names are camel-case.
  • Template type parameters use class not typename.
  • No space between template and < in a template head (discussion).
  • const goes to the left of the type it modifies.
  • &, *, ::*, and ... have no space on their left-hand side, and have a space on their right-hand side unless another operator appears next.
  • Do not insert whitespace between operator and the following token except in a conversion-type-id. Examples: operator(), operator""if, but operator (int(*)()).
  • Use nullptr, not 0, for null pointer values.
  • Names from the standard library are shown without std:: prefix, unless needed for disambiguation. std::move and std::forward are always shown as qualified names.
  • When the specification use boolean conditions in code font, they need to be in the form "a == b is true" or "a == b is false" because otherwise it's just a C++ expression that is required to be well-formed, not necessarily to have a true or false value. The bare expression can be used when we really do want that directly, e.g. Returns: a == b.
  • Mathematical notation can be used for conditions we expect to hold, without needing to say "is true", and they can also be used for real numbers that do not overflow or wrap as C++ integral types do, e.g. A+1 ≥ B is not the same as A+1 >= B.

In a decl-specifier-seq, the decl-specifiers should be written in the following order:

  1. friend / typedef / storage-class-specifier / virtual
  2. inline
  3. constexpr
  4. explicit-specifier
  5. const
  6. volatile
  7. unsigned / signed
  8. short / long
  9. other type-specifiers

The // exposition only marker (\expos in LaTeX) should only appear on the first declaration, i.e. in a synopsis; it should not be repeated on later mentions such as \itemdecls. Similarly, a // freestanding comment should only appear in the context of synopses, and not on \itemdecls.

Class keys for exposition-only classes

Exposition-only member classes have a choice of class key struct or class. As a tentative rule, we recommend using struct only when the class has no invariants (e.g. no private members, but invariants might also result from specification rather than being explicitly visible in the form of members), and class otherwise. We should review how well this rule is working and whether it is worth having.

LaTeX source code formatting

  • Use semantic line breaks in LaTeX source code: http://sembr.org/ and http://rhodesmill.org/brandon/2012/one-sentence-per-line/
  • \rSec precedes any \indextext entries applicable to the entire subclause (#4116).
  • \pnum is at the start of a paragraph, on a line of its own.
  • An introducer for a descriptive element [structure.specification] or a change description marker [diff] is on a line of its own.

A typical paragraph of LaTeX should use the following conventions:

\pnum                                          %% start with \pnum
\indextext{...}%                               %% relevant paragraph-wide entries,
\indextext{...}%                               %% one per line; no whitespace!
Lorem ipsum first sentence here,
defining a \defn{thing}\indextext{alternate entry}.
When needed,                                   %% punctuation (if any) is before the footnote
\begin{footnote}
mind the spacing
\end{footnote}
add a footnote.
\begin{itemize}                                %% No blank line before!
\item Short item 1
\item Short item 2
\item Short item 3
\end{itemize}                                  %% begin and end on their
\begin{note}                                   %% own lines if possible
This is not normative.
\begin{example}
\begin{codeblock}
/* ... */
\end{codeblock}
\end{example}
You can nest examples in notes,
or keep them as consecutive siblings.
\end{note}
This is a very long paragraph, which is not great,
but we want to show off another list:
\begin{itemize}
\item
  For longer list items, indent each item by two.
  No additional blank lines are needed.
\item
  With this style, nested lists are easy as pie.
  \begin{itemize}
  \item Same rules as for all lists,
  \item recursively.
  \end{itemize}
\item
  We have too many lists in the Standard.
\end{itemize}
Finally this paragraph is coming to an end.

Code blocks

Codeblocks follow some loose conventions. Indentation is by two spaces. Indentation is applied when a function declaration contains linebreaks after the template signature or the return type:

template<class T> void foo(T);

template<class T, class Iter>
  std::vector<T> a_longer_function_name(Iter first, Iter last, T& out);

template<class T, class Iter>
  std::vector<std::iterator_traits<Iter>::allocator_type, const T*>
    a_hypnotically_longer_function(Iter first, Iter last, const T& in, T& out) noexcept;

In example code, line comments (//) are indented according to certain rules:

  1. The comment column number must always be a multiple of 4.
  2. There must be at least one space between code and comment.
  3. If possible, there should be at least two spaces between code and comment.
  4. We have a strong preference for columns 20, 32 and 40 (then 48, 64). Text within a region (e.g. a spread or two) may try for a common column. If there is a column value that works well except in a handful of isolated cases, Rule 3 may be violated to accommodate this.

In example code, use // error: reason to highlight an ill-formed line of code, where "reason" could (hypothetically) be produced as an error message from the compiler (note separating colon). In contrast, use // OK, explanation for a well-formed line of code. There is usually no single reason why that line of code is well-formed, but the explanation is just highlighting a particular aspect. #971

The following general rules apply to embedding codeblocks in a sentence: #5035

  • Inline (expressions only): "Something equivalent to f().g()." (The code is entirely engulfed by a regular sentence which ends with a period.)
  • Terminal codeblock: "Equivalent to: [codeblock]". (The regular sentence ends with a colon; the code has no further punctuation.)
  • Codeblock as part of text: "As if [codeblock] but any jump statement appertains to the containing statement." (No punctuation before, within, or after the codeblock. Consider rephrasing the sentence so that punctuation is not actually required in these spots. As a fallback, live with dropping a comma.)

Synopses and itemdecls

  • In a header synopsis and around a class definition, have namespace std {.
  • In a header synopsis, declarations of class and function templates should have a linebreak+indent after the template<...> head. In a class template definition, there is no additional indent after the template<...> head, and the linebreak after the template head is optional.
  • An itemdecl need not be a literal copy/paste of the synopsis entry; usually it can use fewer lines since it is wider. It is permissible to have function templates on a single line here, but it is also fine to retain the linebreak after the template head.
  • Alignment (i.e. inserting extra spaces in the middle of a declaration for matching with preceding or following declarations) is permitted if it improves readability. Use it with good judgement, considering the following guidelines (see #2791 for more discussion):
    • Never apply alignment for declarations appearing in different itemdecls.
    • Align declarations only if each declaration takes at most 2 lines.
    • Only align on =, return type, and open parentheses for (comparison) operator functions.
    • Make sure aligned blocks of declarations have an empty line before and after.

Example

// synopsis

namespace std {
  template<class T>
    struct foo;
  template<class T, class Alloc = allocator<T>>
    class bar;
}

// class definitions

namespace std {
  template<class T> struct foo {
    void abc();
  };

  template<class T, class Alloc = allocator<T>>
  class bar {
  public:
    // \ref{bar.construct} constructors
    bar(int a, int b);
    template<class Iter>
      bar(Iter first, Iter last);
    bar(nullptr_t);
  };
}

\rSec2[bar.construct]{Constructors}

\begin{itemdecl}
bar(int a, int b);
template<class Iter> bar(Iter first, Iter last);
bar(nullptr_t);
\end{itemdecl}

The itemdecl may, but is not required to, use a linebreak after the template head.

ISO Directives

Rules for the overall structure and form of ISO standards is provided by the ISO Directives, Part 2. This covers rules common to all ISO documents, such as the meaning of should and shall, and the referencing of other standards documents. However, the C++ standard provides requirements at two levels simultaneously (for a conforming implementation and for a well-formed program), and these terms are usually repurposed as describing the language rather than the implementation.

Among others, these directives also define the following terms (note capitalization):

  • "International Standard" (standard issued by ISO or IEC); use the phrase "this International Standard" when referring to the C++ standard from within the document itself, in particular when contrasting with earlier revisions of C++ or with C. When referring to the standard as a body of text, for example when explaining the general structure, prefer "this document" (encouraged by recent revisions of the ISO Directives).

LaTeX macro usage

As a general rule, formatting macros should describe the semantics, not the desired visual outcome. For example, \textit is discouraged over \exposid or \grammarterm.

  • \cv{} renders as cv (denoting a possibly empty set of cv-qualifiers)
  • \cvqual{cv1} renders as cv1 (see cv)

Formatting

  • \tcode{U} for placeholders which might occur literally in source code, given a suitable environment. Example: converting the expression \tcode{v} to type \tcode{T} [expr.dynamic.cast] (see #3139)
  • $\tcode{T}_i$ to refer to an indexed type "Ti", for example the i-th type in a parameter pack. Example: [func.bind.bind]
  • \exposid{foo} for exposition-only names
  • \deflibconcept{foo} to define a concept name, `\libconcept{foo} to refer to it (also adds entries to the concepts index)
  • \defexposconcept{foo} to define an exposition-only concept name, \exposconcept{foo} to refer to it (also adds entries to the concepts index)
  • \placeholder{foo} for forming parts of an identifier. Example: atomic_int\placeholder{N}_t [atomics.alias] (see #3271 and #3139)
  • $A$ for other meta-variables. Example: If an operation $A$ that modifies an atomic object $M$ happens before an operation $B$ that modifies $M$, [intro.races] (see #3139)
  • $i^\text{th}$ to spell "i-th" (see #653 and #2000)
  • Dots / ellipsis:
    • Pack expansion in code uses \tcode{...}.
    • Omissions in example code use \commentellip.
    • Comma-separated lists in math mode use \dotsc; binary operators (such as + or -) use \dotsb. Use \cdots when \dotsc (or \ldots) could be confused with a pack expansion. See #2032 and #2529.
    • Consider \vdots (vertical dots) in vertical contexts.
  • Use \tcode{inline} and \tcode{public} only when referring to syntax, not when discussing properties or attributes of functions in general. (For example, member functions defined in the class definition are implicitly inline even when not declared with the inline keyword.)

Spelling conventions

Capitalization

  • Section headings are sentence case, not title case.
  • "[ Note:" and "[ Example:" are not considered to begin a sentence, so the following word should be capitalized as the start of a sentence. Likewise, footnotes should be complete sentences or at least sentence fragments, and should be capitalized as such.

Hyphens: nonX vs. non-X

  • For technical terms defined by the C++ standard, where "nonX" is either not an English word or not one with the right meaning, use a hyphen: non-abstract, non-class, non-const, non-constant, non-empty (when referring to empty classes), non-member, non-trivial (when referring to special member triviality).
  • For plain English words where the "nonX" form is well-established (when the closed form is listed in a common dictionary), do not use a hyphen: nondeterministic, nonempty (when referring to sets), nontrivial (when referring to the difficulty of doing something), and nonzero.
  • For plain English words where the "nonX" form is not established, use a hyphen: non-graphic, non-portable.

Hyphens: Miscellanea

  • Use "well-formed" (with hyphen; its use in the standard is as a self-contained adjective and never as an adverb + past participle combination)

Parentheses and parentheticals

  • When indicating ranges in prose, suffix "(inclusive)" if appropriate (note parentheses, not commas). #2672
  • Use space-separated em-dashes for parentheticals — typed --- in LaTeX — if such parentheticals are desired.

Stable names

  • Avoid special characters in stable names. Full stops are fine and should be the primary means of structuring. Underscores look awkward in our current font and are best avoided. Stable names are restricted to the regex pattern [0-9.a-z]* (enforced by checks).
  • Try not to make stable names too long and complex. They should be recognizable and unique, but some amount of abbreviating is fine (e.g. "mem.res.cons" instead of "memory.resource.constructors").
  • Use "header-name.syn" for synopses of headers (decided in #1991).

Indexes

  • Do not use the tilde (non-breaking space) in index entries. Decided in #1064.
  • Use the following macros for indexing library headers:
    • \libheaderdef{hname} to show <hname> and index it as the definition
    • \indexheader{hname} to index <hname> as the definition
    • \libheader{hname} to show <hname> and index it as a use
    • \libheaderref{hname} to show <hname> (x.y) (with a section reference to hname.syn) and index it as a use
    • \libheaderrefx{hname}{xref} to show <hname> (x.y) (with a section reference to the given xref) and index it as a use
  • Use the following macros for indexing library entities:
    • \indexlibraryglobal{name} for non-member entities (classes, functions, etc.)
    • \indexlibrarymember{member}{class} for class members
    • \indexlibraryctor{class} for constructors
    • \indexlibrarydor{class} for destructors
    • \indexlibraryzombie{name} for zombie names from [zombie.names]
  • Use the following macros for indexing concepts:
    • \deflibconcept{name} to show name and index it as a concept definition
    • \libconcept{name} to show name and index it as a use of the concept
    • \libconceptx{name}{idxentry} to show name (possibly with hyphenation hints) and index idxentry as a use of a concept
    • \defexposconcept{kebab-name} to show kebab-name and index it as a definition for an exposition-only concept
    • \exposconcept{kebab-name} to show kebab-name and index it as a use of an exposition-only concept

Lists

The usual way to present lists is via itemize environments. Nested lists are common, though we should try hard to minimize nesting depth. In rare cases, an enumerated list may be used when the ordering of list items is relevant and cannot be expressed in other ways, though most orderings can be expressed as an ordinary list with connective words like "then".

When an itemized list presents a list of elements connected by "or" or "and", the "or" or "and" can be repeated at the end of each bullet, or just be shown once at the end of the penultimate bullet. To decide, the full context needs to be considered. Lists where each item is short should lean towards omitting the repetition.

Differences with previous versions of C++

Formatting and structure

In the differences with previous versions of C++ (but not in the differences with ISO C!), examples should always be part of the "Effect on original feature" paragraph, using a regular example environment. Explanatory text appertaining to the example can appear immediately after the example.

New headers

When adding headers to the standard, also update the compatibility entry on "new headers".