7 Concepts library [concepts.lib]

7.1 General [concepts.lib.general]

This Clause describes library components that C++ programs may use to perform compile-time validation of template parameters and perform function dispatch based on properties of types. The purpose of these concepts is to establish a foundation for equational reasoning in programs.

The following subclauses describe core language concepts, comparison concepts, object concepts, and function concepts as summarized in Table [tab:concepts.lib.summary].

Table 3 — Fundamental concepts library summary
Subclause Header(s)
[concepts.lib.corelang] Core language concepts <experimental/ranges/concepts>
[concepts.lib.compare] Comparison concepts
[concepts.lib.object] Object concepts
[concepts.lib.callable] Callable concepts

7.1.1 Equality Preservation [concepts.lib.general.equality]

An expression is equality preserving if, given equal inputs, the expression results in equal outputs. The inputs to an expression are the set of the expression's operands. The output of an expression is the expression's result and all operands modified by the expression.

Not all input values must be valid for a given expression; e.g., for integers a and b, the expression a / b is not well-defined when b is 0. This does not preclude the expression a / b being equality preserving. The domain of an expression is the set of input values for which the expression is required to be well-defined.

Expressions required by this specification to be equality preserving are further required to be stable: two evaluations of such an expression with the same input objects must have equal outputs absent any explicit intervening modification of those input objects. [ Note: This requirement allows generic code to reason about the current values of objects based on knowledge of the prior values as observed via equality preserving expressions. It effectively forbids spontaneous changes to an object, changes to an object from another thread of execution, changes to an object as side effects of non-modifying expressions, and changes to an object as side effects of modifying a distinct object if those changes could be observable to a library function via an equality preserving expression that is required to be valid for that object.  — end note ]

Expressions declared in a requires-expression in this document are required to be equality preserving, except for those annotated with the comment “not required to be equality preserving.” An expression so annotated may be equality preserving, but is not required to be so.

An expression that may alter the value of one or more of its inputs in a manner observable to equality preserving expressions is said to modify those inputs. This document uses a notational convention to specify which expressions declared in a requires-expression modify which inputs: except where otherwise specified, an expression operand that is a non-constant lvalue or rvalue may be modified. Operands that are constant lvalues or rvalues must not be modified.

Where a requires-expression declares an expression that is non-modifying for some constant lvalue operand, additional variations of that expression that accept a non-constant lvalue or (possibly constant) rvalue for the given operand are also required except where such an expression variation is explicitly required with differing semantics. These implicit expression variations must meet the semantic requirements of the declared expression. The extent to which an implementation validates the syntax of the variations is unspecified.

Example:

template <class T>
concept bool C =
  requires(T a, T b, const T c, const T d) {
    c == d;           // #1
    a = std::move(b); // #2
    a = c;            // #3
  };

Expression #1 does not modify either of its operands, #2 modifies both of its operands, and #3 modifies only its first operand a.

Expression #1 implicitly requires additional expression variations that meet the requirements for c == d (including non-modification), as if the expressions

a == d;       a == b;             a == move(b);       a == d;
c == a;       c == move(a);       c == move(d);
move(a) == d; move(a) == b;       move(a) == move(b); move(a) == move(d);
move(c) == b; move(c) == move(b); move(c) == d;       move(c) == move(d);

had been declared as well.

Expression #3 implicitly requires additional expression variations that meet the requirements for a = c (including non-modification of the second operand), as if the expressions a = b and a = move(c) had been declared. Expression #3 does not implicitly require an expression variation with a non-constant rvalue second operand, since expression #2 already specifies exactly such an expression explicitly.  — end example ]

Example: The following type T meets the explicitly stated syntactic requirements of concept C above but does not meet the additional implicit requirements:

struct T {
  bool operator==(const T&) const { return true; }
  bool operator==(T&) = delete;
};

T fails to meet the implicit requirements of C, so C<T> is not satisfied. Since implementations are not required to validate the syntax of implicit requirements, it is unspecified whether or not an implementation diagnoses as ill-formed a program which requires C<T>.  — end example ]