24 Containers library [containers]

24.2 Requirements [container.requirements]

24.2.2 General containers [container.gen.reqmts]

24.2.2.1 General [container.requirements.general]

In subclause [container.gen.reqmts],
  • X denotes a container class containing objects of type T,
  • a and b denote values of type X,
  • i and j denote values of type (possibly const) X​::​iterator,
  • u denotes an identifier,
  • r denotes a non-const value of type X, and
  • rv denotes a non-const rvalue of type X.

24.2.2.2 Containers [container.reqmts]

A type X meets the container requirements if the following types, statements, and expressions are well-formed and have the specified semantics.
typename X::value_type
Result: T
Preconditions: T is Cpp17Erasable from X (see [container.alloc.reqmts], below).
typename X::reference
Result: T&
typename X::const_reference
Result: const T&
typename X::iterator
Result: A type that meets the forward iterator requirements ([forward.iterators]) with value type T.
The type X​::​iterator is convertible to X​::​const_­iterator.
typename X::const_iterator
Result: A type that meets the requirements of a constant iterator and those of a forward iterator with value type T.
typename X::difference_type
Result: A signed integer type, identical to the difference type of X​::​iterator and X​::​const_­iterator.
typename X::size_type
Result: An unsigned integer type that can represent any non-negative value of X​::​difference_­type.
X u; X u = X();
Postconditions: u.empty()
Complexity: Constant.
X u(a); X u = a;
Preconditions: T is Cpp17CopyInsertable into X (see below).
Postconditions: u == a
Complexity: Linear.
X u(rv); X u = rv;
Postconditions: u is equal to the value that rv had before this construction.
Complexity: Linear for array and constant for all other standard containers.
a = rv
Result: X&.
Effects: All existing elements of a are either move assigned to or destroyed.
Postconditions: If a and rv do not refer to the same object, a is equal to the value that rv had before this assignment.
Complexity: Linear.
a.~X()
Result: void
Effects: Destroys every element of a; any memory obtained is deallocated.
Complexity: Linear.
a.begin()
Result: iterator; const_­iterator for constant a.
Returns: An iterator referring to the first element in the container.
Complexity: Constant.
a.end()
Result: iterator; const_­iterator for constant a.
Returns: An iterator which is the past-the-end value for the container.
Complexity: Constant.
a.cbegin()
Result: const_­iterator.
Returns: const_­cast<X const&>(a).begin()
Complexity: Constant.
a.cend()
Result: const_­iterator.
Returns: const_­cast<X const&>(a).end()
Complexity: Constant.
i <=> j
Result: strong_­ordering.
Constraints: X​::​iterator meets the random access iterator requirements.
Complexity: Constant.
a == b
Preconditions: T meets the Cpp17EqualityComparable requirements.
Result: Convertible to bool.
Returns: equal(a.begin(), a.end(), b.begin(), b.end())
[Note 1:
The algorithm equal is defined in [alg.equal].
— end note]
Complexity: Constant if a.size() != b.size(), linear otherwise.
Remarks: == is an equivalence relation.
a != b
Effects: Equivalent to !(a == b).
a.swap(b)
Result: void
Effects: Exchanges the contents of a and b.
Complexity: Linear for array and constant for all other standard containers.
swap(a, b)
Effects: Equivalent to a.swap(b).
r = a
Result: X&.
Postconditions: r == a.
Complexity: Linear.
a.size()
Result: size_­type.
Returns: distance(a.begin(), a.end()), i.e. the number of elements in the container.
Complexity: Constant.
Remarks: The number of elements is defined by the rules of constructors, inserts, and erases.
a.max_size()
Result: size_­type.
Returns: distance(begin(), end()) for the largest possible container.
Complexity: Constant.
a.empty()
Result: Convertible to bool.
Returns: a.begin() == a.end()
Complexity: Constant.
Remarks: If the container is empty, then a.empty() is true.
In the expressions i == j i != j i < j i <= j i >= j i > j i <=> j i - j where i and j denote objects of a container's iterator type, either or both may be replaced by an object of the container's const_­iterator type referring to the same element with no change in semantics.
Unless otherwise specified, all containers defined in this Clause obtain memory using an allocator (see [allocator.requirements]).
[Note 2:
In particular, containers and iterators do not store references to allocated elements other than through the allocator's pointer type, i.e., as objects of type P or pointer_­traits<P>​::​template rebind<unspecified>, where P is allocator_­traits<allocator_­type>​::​pointer.
— end note]
Copy constructors for these container types obtain an allocator by calling allocator_­traits<allocator_­type>​::​select_­on_­container_­copy_­construction on the allocator belonging to the container being copied.
Move constructors obtain an allocator by move construction from the allocator belonging to the container being moved.
Such move construction of the allocator shall not exit via an exception.
All other constructors for these container types take a const allocator_­type& argument.
[Note 3:
If an invocation of a constructor uses the default value of an optional allocator argument, then the allocator type must support value-initialization.
— end note]
A copy of this allocator is used for any memory allocation and element construction performed, by these constructors and by all member functions, during the lifetime of each container object or until the allocator is replaced.
The allocator may be replaced only via assignment or swap().
Allocator replacement is performed by copy assignment, move assignment, or swapping of the allocator only if
  • allocator_­traits<allocator_­type>​::​propagate_­on_­container_­copy_­assignment​::​value,
  • allocator_­traits<allocator_­type>​::​propagate_­on_­container_­move_­assignment​::​value, or
  • allocator_­traits<allocator_­type>​::​propagate_­on_­container_­swap​::​value
is true within the implementation of the corresponding container operation.
In all container types defined in this Clause, the member get_­allocator() returns a copy of the allocator used to construct the container or, if that allocator has been replaced, a copy of the most recent replacement.
The expression a.swap(b), for containers a and b of a standard container type other than array, shall exchange the values of a and b without invoking any move, copy, or swap operations on the individual container elements.
Lvalues of any Compare, Pred, or Hash types belonging to a and b shall be swappable and shall be exchanged by calling swap as described in [swappable.requirements].
If allocator_­traits<allocator_­type>​::​propagate_­on_­container_­swap​::​value is true, then lvalues of type allocator_­type shall be swappable and the allocators of a and b shall also be exchanged by calling swap as described in [swappable.requirements].
Otherwise, the allocators shall not be swapped, and the behavior is undefined unless a.get_­allocator() == b.get_­allocator().
Every iterator referring to an element in one container before the swap shall refer to the same element in the other container after the swap.
It is unspecified whether an iterator with value a.end() before the swap will have value b.end() after the swap.

24.2.2.3 Reversible container requirements [container.rev.reqmts]

A type X meets the reversible container requirements if X meets the container requirements, the iterator type of X belongs to the bidirectional or random access iterator categories ([iterator.requirements]), and the following types and expressions are well-formed and have the specified semantics.
typename X::reverse_iterator
Result: The type reverse_­iterator<X​::​iterator>, an iterator type whose value type is T.
typename X::const_reverse_iterator
Result: The type reverse_­iterator<X​::​const_­iterator>, a constant iterator type whose value type is T.
a.rbegin()
Result: reverse_­iterator; const_­reverse_­iterator for constant a.
Returns: reverse_­iterator(end())
Complexity: Constant.
a.rend()
Result: reverse_­iterator; const_­reverse_­iterator for constant a.
Returns: reverse_­iterator(begin())
Complexity: Constant.
a.crbegin()
Result: const_­reverse_­iterator.
Returns: const_­cast<X const&>(a).rbegin()
Complexity: Constant.
a.crend()
Result: const_­reverse_­iterator.
Returns: const_­cast<X const&>(a).rend()
Complexity: Constant.
Unless otherwise specified (see [associative.reqmts.except], [unord.req.except], [deque.modifiers], and [vector.modifiers]) all container types defined in this Clause meet the following additional requirements:
  • if an exception is thrown by an insert() or emplace() function while inserting a single element, that function has no effects.
  • if an exception is thrown by a push_­back(), push_­front(), emplace_­back(), or emplace_­front() function, that function has no effects.
  • no erase(), clear(), pop_­back() or pop_­front() function throws an exception.
  • no copy constructor or assignment operator of a returned iterator throws an exception.
  • no swap() function throws an exception.
  • no swap() function invalidates any references, pointers, or iterators referring to the elements of the containers being swapped.
    [Note 1:
    The end() iterator does not refer to any element, so it can be invalidated.
    — end note]
Unless otherwise specified (either explicitly or by defining a function in terms of other functions), invoking a container member function or passing a container as an argument to a library function shall not invalidate iterators to, or change the values of, objects within that container.
A contiguous container is a container whose member types iterator and const_­iterator meet the Cpp17RandomAccessIterator requirements ([random.access.iterators]) and model contiguous_­iterator ([iterator.concept.contiguous]).

24.2.2.4 Optional container requirements [container.opt.reqmts]

The following operations are provided for some types of containers but not others.
Those containers for which the listed operations are provided shall implement the semantics as described unless otherwise stated.
If the iterators passed to lexicographical_­compare_­three_­way meet the constexpr iterator requirements ([iterator.requirements.general]) then the operations described below are implemented by constexpr functions.
a <=> b
Result: synth-three-way-result<X​::​value_­type>.
Preconditions: Either <=> is defined for values of type (possibly const) T, or < is defined for values of type (possibly const) T and < is a total ordering relationship.
Returns: lexicographical_­compare_­three_­way(a.begin(), a.end(), b.begin(), b.end(),
synth-three-way)
[Note 1:
The algorithm lexicographical_­compare_­three_­way is defined in [algorithms].
— end note]
Complexity: Linear.

24.2.2.5 Allocator-aware containers [container.alloc.reqmts]

All of the containers defined in [containers] and in [basic.string] except array meet the additional requirements of an allocator-aware container, as described below.
Given an allocator type A and given a container type X having a value_­type identical to T and an allocator_­type identical to allocator_­traits<A>​::​rebind_­alloc<T> and given an lvalue m of type A, a pointer p of type T*, an expression v of type (possibly const) T, and an rvalue rv of type T, the following terms are defined.
If X is not allocator-aware or is a specialization of basic_­string, the terms below are defined as if A were allocator<T> — no allocator object needs to be created and user specializations of allocator<T> are not instantiated:
  • T is Cpp17DefaultInsertable into X means that the following expression is well-formed: allocator_traits<A>::construct(m, p)
  • An element of X is default-inserted if it is initialized by evaluation of the expression allocator_traits<A>::construct(m, p) where p is the address of the uninitialized storage for the element allocated within X.
  • T is Cpp17MoveInsertable into X means that the following expression is well-formed: allocator_traits<A>::construct(m, p, rv) and its evaluation causes the following postcondition to hold: The value of *p is equivalent to the value of rv before the evaluation.
    [Note 1:
    rv remains a valid object.
    Its state is unspecified
    — end note]
  • T is Cpp17CopyInsertable into X means that, in addition to T being Cpp17MoveInsertable into X, the following expression is well-formed: allocator_traits<A>::construct(m, p, v) and its evaluation causes the following postcondition to hold: The value of v is unchanged and is equivalent to *p.
  • T is Cpp17EmplaceConstructible into X from args, for zero or more arguments args, means that the following expression is well-formed: allocator_traits<A>::construct(m, p, args)
  • T is Cpp17Erasable from X means that the following expression is well-formed: allocator_traits<A>::destroy(m, p)
[Note 2:
A container calls allocator_­traits<A>​::​construct(m, p, args) to construct an element at p using args, with m == get_­allocator().
The default construct in allocator will call ​::​new((void*)p) T(args), but specialized allocators can choose a different definition.
— end note]
The following exposition-only concept is used in the definition of containers: template<class R, class T> concept container-compatible-range = // exposition only ranges::input_­range<R> && convertible_­to<ranges::range_reference_t<R>, T>;
In this subclause,
  • X denotes an allocator-aware container class with a value_­type of T using an allocator of type A,
  • u denotes a variable,
  • a and b denote non-const lvalues of type X,
  • c denotes an lvalue of type const X,
  • t denotes an lvalue or a const rvalue of type X,
  • rv denotes a non-const rvalue of type X, and
  • m is a value of type A.
A type X meets the allocator-aware container requirements if X meets the container requirements and the following types, statements, and expressions are well-formed and have the specified semantics.
typename X::allocator_type
Result: A
Preconditions: allocator_­type​::​value_­type is the same as X​::​value_­type.
c.get_allocator()
Result: A
Complexity: Constant.
X u; X u = X();
Preconditions: A meets the Cpp17DefaultConstructible requirements.
Postconditions: u.empty() returns true, u.get_­allocator() == A().
Complexity: Constant.
X u(m);
Postconditions: u.empty() returns true, u.get_­allocator() == m.
Complexity: Constant.
X u(t, m);
Preconditions: T is Cpp17CopyInsertable into X.
Postconditions: u == t, u.get_­allocator() == m
Complexity: Linear.
X u(rv);
Postconditions: u has the same elements as rv had before this construction; the value of u.get_­allocator() is the same as the value of rv.get_­allocator() before this construction.
Complexity: Constant.
X u(rv, m);
Preconditions: T is Cpp17MoveInsertable into X.
Postconditions: u has the same elements, or copies of the elements, that rv had before this construction, u.get_­allocator() == m.
Complexity: Constant if m == rv.get_­allocator(), otherwise linear.
a = t
Result: X&.
Preconditions: T is Cpp17CopyInsertable into X and Cpp17CopyAssignable.
Postconditions: a == t is true.
Complexity: Linear.
a = rv
Result: X&.
Preconditions: If allocator_­traits<allocator_­type>​::​propagate_­on_­container_­move_­assignment​::​value is false, T is Cpp17MoveInsertable into X and Cpp17MoveAssignable.
Effects: All existing elements of a are either move assigned to or destroyed.
Postconditions: If a and rv do not refer to the same object, a is equal to the value that rv had before this assignment.
Complexity: Linear.
a.swap(b)
Result: void
Effects: Exchanges the contents of a and b.
Complexity: Constant.
The behavior of certain container member functions and deduction guides depends on whether types qualify as input iterators or allocators.
The extent to which an implementation determines that a type cannot be an input iterator is unspecified, except that as a minimum integral types shall not qualify as input iterators.
Likewise, the extent to which an implementation determines that a type cannot be an allocator is unspecified, except that as a minimum a type A shall not qualify as an allocator unless it meets both of the following conditions:
  • The qualified-id A​::​value_­type is valid and denotes a type ([temp.deduct]).
  • The expression declval<A&>().allocate(size_­t{}) is well-formed when treated as an unevaluated operand.