17 Library introduction [library]

17.6 Library-wide requirements [requirements]

17.6.3 Requirements on types and expressions [utility.requirements]

[utility.arg.requirements] describes requirements on types and expressions used to instantiate templates defined in the C++ standard library. [swappable.requirements] describes the requirements on swappable types and swappable expressions. [nullablepointer.requirements] describes the requirements on pointer-like types that support null values. [hash.requirements] describes the requirements on hash function objects. [allocator.requirements] describes the requirements on storage allocators.

17.6.3.1 Template argument requirements [utility.arg.requirements]

The template definitions in the C++ standard library refer to various named requirements whose details are set out in tables [equalitycomparable][destructible]. In these tables, T is an object or reference type to be supplied by a C++ program instantiating a template; a, b, and c are values of type (possibly const) T; s and t are modifiable lvalues of type T; u denotes an identifier; rv is an rvalue of type T; and v is an lvalue of type (possibly const) T or an rvalue of type const T.

In general, a default constructor is not required. Certain container class member function signatures specify T() as a default argument. T() shall be a well-defined expression ([dcl.init]) if one of those signatures is called using the default argument ([dcl.fct.default]).

Table 17EqualityComparable requirements
Expression Return type Requirement
a == b convertible to bool == is an equivalence relation, that is, it has the following properties:
  • For all a, a == a.

  • If a == b, then b == a.

  • If a == b and b == c, then a == c.

Table 18LessThanComparable requirements
Expression Return type Requirement
a < b convertible to bool < is a strict weak ordering relation ([alg.sorting])

Table 19DefaultConstructible requirements
Expression Post-condition
T t; object t is default-initialized
T u{}; object u is value-initialized
T() a temporary object of type T is value-initialized
T{}

Table 20MoveConstructible requirements
Expression Post-condition
T u = rv; u is equivalent to the value of rv before the construction
T(rv) T(rv) is equivalent to the value of rv before the construction
rv's state is unspecified [ Note: rv must still meet the requirements of the library component that is using it. The operations listed in those requirements must work as specified whether rv has been moved from or not.  — end note ]

Table 21CopyConstructible requirements (in addition to MoveConstructible)
Expression Post-condition
T u = v; the value of v is unchanged and is equivalent to u
T(v) the value of v is unchanged and is equivalent to T(v)

Table 22MoveAssignable requirements
Expression Return type Return value Post-condition
t = rv T& t t is equivalent to the value of rv before the assignment
rv's state is unspecified. [ Note: rv must still meet the requirements of the library component that is using it. The operations listed in those requirements must work as specified whether rv has been moved from or not.  — end note ]

Table 23CopyAssignable requirements (in addition to MoveAssignable)
Expression Return type Return value Post-condition
t = v T& t t is equivalent to v, the value of v is unchanged

Table 24Destructible requirements
Expression Post-condition
u.~T() All resources owned by u are reclaimed, no exception is propagated.

17.6.3.2 Swappable requirements [swappable.requirements]

This subclause provides definitions for swappable types and expressions. In these definitions, let t denote an expression of type T, and let u denote an expression of type U.

An object t is swappable with an object u if and only if:

  • the expressions swap(t, u) and swap(u, t) are valid when evaluated in the context described below, and

  • these expressions have the following effects:

    • the object referred to by t has the value originally held by u and

    • the object referred to by u has the value originally held by t.

The context in which swap(t, u) and swap(u, t) are evaluated shall ensure that a binary non-member function named “swap” is selected via overload resolution ([over.match]) on a candidate set that includes:

Note: If T and U are both fundamental types or arrays of fundamental types and the declarations from the header <utility> are in scope, the overall lookup set described above is equivalent to that of the qualified name lookup applied to the expression std::swap(t, u) or std::swap(u, t) as appropriate.  — end note ]

Note: It is unspecified whether a library component that has a swappable requirement includes the header <utility> to ensure an appropriate evaluation context.  — end note ]

An rvalue or lvalue t is swappable if and only if t is swappable with any rvalue or lvalue, respectively, of type T.

A type X satisfying any of the iterator requirements ([iterator.requirements]) is ValueSwappable if, for any dereferenceable object x of type X, *x is swappable.

Example: User code can ensure that the evaluation of swap calls is performed in an appropriate context under the various conditions as follows:

#include <utility>

// Requires: std::forward<T>(t) shall be swappable with std::forward<U>(u).
template <class T, class U>
void value_swap(T&& t, U&& u) {
  using std::swap;
  swap(std::forward<T>(t), std::forward<U>(u)); // OK: uses “swappable with” conditions
                                                // for rvalues and lvalues
}

// Requires: lvalues of T shall be swappable.
template <class T>
void lv_swap(T& t1 T& t2) {
  using std::swap;
  swap(t1, t2);                                 // OK: uses swappable conditions for
}                                               // lvalues of type T

namespace N {
  struct A { int m; };
  struct Proxy { A *a; };
  Proxy proxy(A& a) { return Proxy{ &a }; }

  void swap(A& x, Proxy p) {
    std::swap(x.m, p.a->m);                     // OK: uses context equivalent to swappable
                                                // conditions for fundamental types
  }
  void swap(Proxy p, A& x) { swap(x, p); }      // satisfy symmetry constraint
}

int main() {
  int i = 1, j = 2;
  lv_swap(i, j);
  assert(i == 2 && j == 1);

  N::A a1 = { 5 }, a2 = { -5 };
  value_swap(a1, proxy(a2));
  assert(a1.m == -5 && a2.m == 5);
}

 — end example ]

17.6.3.3 NullablePointer requirements [nullablepointer.requirements]

A NullablePointer type is a pointer-like type that supports null values. A type P meets the requirements of NullablePointer if:

  • P satisfies the requirements of EqualityComparable, DefaultConstructible, CopyConstructible, CopyAssignable, and Destructible,

  • lvalues of type P are swappable ([swappable.requirements]),

  • the expressions shown in Table [nullablepointer] are valid and have the indicated semantics, and

  • P satisfies all the other requirements of this subclause.

A value-initialized object of type P produces the null value of the type. The null value shall be equivalent only to itself. A default-initialized object of type P may have an indeterminate value. [ Note: Operations involving indeterminate values may cause undefined behavior.  — end note ]

An object p of type P can be contextually converted to bool (Clause [conv]). The effect shall be as if p != nullptr had been evaluated in place of p.

No operation which is part of the NullablePointer requirements shall exit via an exception.

In Table [nullablepointer], u denotes an identifier, t denotes a non-const lvalue of type P, a and b denote values of type (possibly const) P, and np denotes a value of type (possibly const) std::nullptr_t.

Table 25NullablePointer requirements
Expression Return type Operational semantics
P u(np);
post: u == nullptr
P u = np;
P(np) post: P(np) == nullptr
t = np P& post: t == nullptr
a != b contextually convertible to bool !(a == b)
a == np contextually convertible to bool a == P()
np == a
a != np contextually convertible to bool !(a == np)
np != a

17.6.3.4 Hash requirements [hash.requirements]

A type H meets the Hash requirements if:

Given Key is an argument type for function objects of type H, in Table [hash] h is a value of type (possibly const) H, u is an lvalue of type Key, and k is a value of a type convertible to (possibly const) Key.

Table 26Hash requirements
Expression Return type Requirement
h(k) size_t The value returned shall depend only on the argument k. [ Note: Thus all evaluations of the expression h(k) with the same value for k yield the same result.  — end note ] [ Note: For two different values t1 and t2, the probability that h(t1) and h(t2) compare equal should be very small, approaching 1.0 / numeric_limits<size_t>::max().  — end note ]
h(u) size_t Shall not modify u.

17.6.3.5 Allocator requirements [allocator.requirements]

The library describes a standard set of requirements for allocators, which are class-type objects that encapsulate the information about an allocation model. This information includes the knowledge of pointer types, the type of their difference, the type of the size of objects in this allocation model, as well as the memory allocation and deallocation primitives for it. All of the string types (Clause [strings]), containers (Clause [containers]) (except array), string buffers and string streams (Clause [input.output]), and match_results (Clause [re]) are parameterized in terms of allocators.

The template struct allocator_traits ([allocator.traits]) supplies a uniform interface to all allocator types. Table [tab:desc.var.def] describes the types manipulated through allocators. Table [tab:utilities.allocator.requirements] describes the requirements on allocator types and thus on types used to instantiate allocator_traits. A requirement is optional if the last column of Table [tab:utilities.allocator.requirements] specifies a default for a given expression. Within the standard library allocator_traits template, an optional requirement that is not supplied by an allocator is replaced by the specified default expression. A user specialization of allocator_traits may provide different defaults and may provide defaults for different requirements than the primary template. Within Tables [tab:desc.var.def] and [tab:utilities.allocator.requirements], the use of move and forward always refers to std::move and std::forward, respectively.

Table 27 — Descriptive variable definitions
VariableDefinition
T, U, C any non-const object type ([basic.types])
V a type convertible to T
X an Allocator class for type T
Y the corresponding Allocator class for type U
XX the type allocator_traits<X>
YY the type allocator_traits<Y>
t a value of type const T&
a, a1, a2 values of type X&
a3 an rvalue of type X
b a value of type Y
c a dereferenceable pointer of type C*
p a value of type XX::pointer, obtained by calling a1.allocate, where a1 == a
q a value of type XX::const_pointer obtained by conversion from a value p.
w a value of type XX::void_pointer obtained by conversion from a value p
z a value of type XX::const_void_pointer obtained by conversion from a value q or a value w
r a value of type T& obtained by the expression *p.
s a value of type const T& obtained by the expression *q or by conversion from a value r.
u a value of type YY::const_pointer obtained by calling YY::allocate, or else nullptr.
v a value of type V
n a value of type XX::size_type.
Args a template parameter pack
args a function parameter pack with the pattern Args&&
Table 28 — Allocator requirements
ExpressionReturn typeAssertion/noteDefault
pre-/post-condition
X::pointer T*
X::const_pointer X::pointer is convertible to X::const_pointer pointer_traits<X::pointer>::rebind<const T>
X::void_pointer
Y::void_pointer
X::pointer is convertible to X::void_pointer. X::void_pointer and Y::void_pointer are the same type. pointer_traits<X::pointer>::rebind<void>
X::const_void_pointer
Y::const_void_pointer
X::pointer, X::const_pointer, and X::void_pointer are convertible to X::const_void_pointer. X::const_void_pointer and Y::const_void_pointer are the same type. pointer_traits<X::pointer>::rebind<const void>
X::value_type Identical to T
X::size_type unsigned integer type a type that can represent the size of the largest object in the allocation model. make_unsigned<X::difference_type>::type
X::difference_type signed integer type a type that can represent the difference between any two pointers in the allocation model. pointer_traits<X::pointer>::difference_type
typename X::template rebind<U>::other Y For all U (including T), Y::template rebind<T>::other is X. See Note A, below.
*p T&
*q const T& *q refers to the same object as *p
p->m type of T::m pre: (*p).m is well-defined. equivalent to (*p).m
q->m type of T::m pre: (*q).m is well-defined. equivalent to (*q).m
static_- cast<X::pointer>(w) X::pointer static_cast<X::pointer>(w) == p
static_cast<X ::const_pointer>(z) X::const_pointer static_cast<X ::const_pointer>(z) == q
a.allocate(n) X::pointer Memory is allocated for n objects of type T but objects are not constructed. allocate may raise an appropriate exception.181Note: If n == 0, the return value is unspecified.  — end note ]
a.allocate(n, u) X::pointer Same as a.allocate(n). The use of u is unspecified, but it is intended as an aid to locality. a.allocate(n)
a.deallocate(p,n) (not used) All n T objects in the area pointed to by p shall be destroyed prior to this call. n shall match the value passed to allocate to obtain this memory. Does not throw exceptions. [ Note: p shall not be singular. — end note ]
a.max_size() X::size_type the largest value that can meaningfully be passed to X::allocate() numeric_limits<size_type>::max()
a1 == a2 bool returns true only if storage allocated from each can be deallocated via the other. operator== shall be reflexive, symmetric, and transitive, and shall not exit via an exception.
a1 != a2 bool same as !(a1 == a2)
a == b bool same as a == Y::rebind<T>::other(b)
a != b bool same as !(a == b)
X a1(a); Shall not exit via an exception.
post: a1 == a
X a(b); Shall not exit via an exception.
post: Y(a) == b, a == X(b)
X a1(move(a)); Shall not exit via an exception.
post: a1 equals the prior value of a.
X a(move(b)); Shall not exit via an exception.
post: a equals the prior value of X(b).
a.construct(c, args) (not used) Effect: Constructs an object of type C at c ::new ((void*)c) C(forward<Args>(args)...)
a.destroy(c) (not used) Effect: Destroys the object at c c->~C()
a.select_on_container_copy_construction() X Typically returns either a or X() return a;
X::propagate_on_container_copy_assignment Identical to or derived from true_type or false_type true_type only if an allocator of type X should be copied when the client container is copy-assigned. false_type
X::propagate_on_container_move_assignment Identical to or derived from true_type or false_type true_type only if an allocator of type X should be moved when the client container is move-assigned. false_type
X::propagate_on_- container_swap Identical to or derived from true_type or false_type true_type only if an allocator of type X should be swapped when the client container is swapped. false_type

Note A: The member class template rebind in the table above is effectively a typedef template. [ Note: In general, if the name Allocator is bound to SomeAllocator<T>, then Allocator::rebind<U>::other is the same type as SomeAllocator<U>, where SomeAllocator<T>::value_type is T and SomeAllocator<U>::value_type is U.  — end note ] If Allocator is a class template instantiation of the form SomeAllocator<T, Args>, where Args is zero or more type arguments, and Allocator does not supply a rebind member template, the standard allocator_traits template uses SomeAllocator<U, Args> in place of Allocator::rebind<U>::other by default. For allocator types that are not template instantiations of the above form, no default is provided.

The X::pointer, X::const_pointer, X::void_pointer, and X::const_void_pointer types shall satisfy the requirements of NullablePointer ([nullablepointer.requirements]). No constructor, comparison operator, copy operation, move operation, or swap operation on these types shall exit via an exception. X::pointer and X::const_pointer shall also satisfy the requirements for a random access iterator ([iterator.requirements]).

An allocator may constrain the types on which it can be instantiated and the arguments for which its construct member may be called. If a type cannot be used with a particular allocator, the allocator class or the call to construct may fail to instantiate.

Example: the following is an allocator class template supporting the minimal interface that satisfies the requirements of Table [tab:utilities.allocator.requirements]:

template <class Tp>
struct SimpleAllocator {
  typedef Tp value_type;
  SimpleAllocator(ctor args);

  template <class T> SimpleAllocator(const SimpleAllocator<T>& other);

  Tp *allocate(std::size_t n);
  void deallocate(Tp *p, std::size_t n);
};

 — end example ]

If the alignment associated with a specific over-aligned type is not supported by an allocator, instantiation of the allocator for that type may fail. The allocator also may silently ignore the requested alignment. [ Note: Additionally, the member function allocate for that type may fail by throwing an object of type std::bad_alloc. — end note ]

It is intended that a.allocate be an efficient means of allocating a single object of type T, even when sizeof(T) is small. That is, there is no need for a container to maintain its own free list.