constexpr containers and adaptors

This paper proposes marking all containers and adaptors constexpr (with exception of std::hive). This will make compile time programming much easier by making normal C++ and constexpr C++ more similar.

This paper doesn't propose changes in std::hash.

Revision history

Introduction and motivation

All necessary constexpr functionality required by the containers to be changed is supported by the language. There is no reason for them not to be constexpr, and the standard library should support these in order for them to properly serve as vocabulary types.

The following table shows what this paper proposes, and the status quo:

container / adapterbeforeafternote
std::arrayβœ”οΈŽβœ”οΈŽsince C++11/14
std::dequexβœ”οΈŽ
std::forward_listxβœ”οΈŽ
std::listxβœ”οΈŽ
std::vectorβœ”οΈŽβœ”οΈŽsince C++20 🍫
std::mapxβœ”οΈŽusing node_handle::key() is UB (CWG-2514)
std::multimapxβœ”οΈŽusing node_handle::key() is UB (CWG-2514)
std::setxβœ”οΈŽ
std::multisetxβœ”οΈŽ
std::unordered_mapxβœ”οΈŽusing node_handle::key() is UB (CWG-2514, a custom hash function needed)
std::unordered_multimapxβœ”οΈŽusing node_handle::key() is UB (CWG-2514, a custom hash function needed)
std::unordered_setxβœ”οΈŽ(a custom hash function needed)
std::unordered_multisetxβœ”οΈŽ(a custom hash function needed)
std::queuexβœ”οΈŽ
std::priority_queuexβœ”οΈŽ
std::stackxβœ”οΈŽ
std::flat_mapxβœ”οΈŽnew adapter in C++23, originally proposed for C++20
std::flat_multimapxβœ”οΈŽnew adapter in C++23, originally proposed for C++20
std::flat_setxβœ”οΈŽnew adapter in C++23, originally proposed for C++20
std::flat_multisetxβœ”οΈŽnew adapter in C++23, originally proposed for C++20
std::spanβœ”οΈŽβœ”οΈŽ
std::mdspanβœ”οΈŽβœ”οΈŽ
std::basic_stringβœ”οΈŽβœ”οΈŽsince C++20 🍫
std::basic_string_viewβœ”οΈŽβœ”οΈŽsince C++17

What is not included

std::hash

Unordered containers needs a hashing facility. But default provided std::hash can't be made constexpr friendly as Cpp17Hash requirements states its result is guaranteed to be consistent only within duration of the program. Intention is to allow salting the hash to avoid cache poisoning attacks. The requirement is in a direct conflict with calculating the hash within constant evaluation. One possible solution would be creating a std::stable_hash facility without such requirement. But this is a can of worms outside of scope of this paper and I'm not brave enough to open it.

Therefore this paper doesn't propose any change for std::hash. Users can still use unordered containers when they provide their own hashing facility. This situation is not ideal but much better than status quo of not having any such container available at all.

std::hive

At time of writing this paper std::hive is not part of the standard draft with stable wording and it's not part of this paper.

Non-transient allocations

The standard currently disallows any allocation to "leak" out of constant evaluation [expr.const]. This paper doesn't change anything about this. You still can't use any of allocating containers in a constexpr variable.

Implementation experience

I implemented the proposal in a personal fork of libc++ by marking all functions and member functions associated with containers with constexpr. Containers deque, forward_list, list, and all adaptors (note: libc++ doesn't have flat_map and flat_set yet) were modified without any problem.

When marking set, map, unordered_set, and unordered_map I ran into problems with UB in the construction of nodes, where the node is allocated but not constructed and only its subobject with value is constructed. Another UB I ran into was type-punning via union and static_cast from size_t into unsigned char when calculating hash; both these issues were fixed by using std::bit_cast instead. This is actually a good argument for constexpr ALL the things as proper testing during constant evaluation can easily catch such problems.

Here are examples with iterators and here are examples with normal operations.

std::hash<T *>

As part of implementation I tried to make std::hash completely constexpr, biggest problem I ran into was hashing of pointers. As constant evaluator evaluate symbolically a pointer to allocated memory (which can't survive to runtime) or to static symbol are of different values than in runtime.

I was able to create a modification to bitcast builtin to give me a unique integer representation of the pointer. But this was an exercise in futility as allowing such hashing (even if we have stable hashing) would lead to inconsistency between constexpr and runtime hash of same object. And this is something undesirable.

std::less<T *> and friends

Similar problem as with std::hash is present in std::less and other comparison functors too. Interpreter of constant evaluated code doesn't know addresses of objects during normal execution of program (subject to optimizer, linker, and ASLR changes). If we allow comparing pointers with an implementation specific strict total ordering (as specified for functors and currently not implementable for constant evaluation) the final ordering of pointers won't be same as in runtime.

key & node_handle::key()

Another problem I ran into is the already existing CWG-2514 where node_handle::key() returns non-const reference to key which is const inside node_type. This disallows making the member function constexpr and is not included in proposal. If solutuon of CWG-2514 finds solution how to fix the problem without having UB then constexpr should be added as part of the resolution.

It was recommended by a member of LEWG to remove all constexpr functionality from node-handle but I find the functionality even limited to be better than not having it available as point of making things constexpr is to remove arbitrary limitations.

libstdc++

Parts of implementation of containers are in .cc files instead of headers and needs to be moved there:

  • Containers deque (_Deque_base), forward_list (_Fwd_list_node_base), list (_List_node_base) have defined their node bases in .cc files.
  • Associative containers set, multi_set, map, and multi_map are using Red-Black Tree implementation in a .cc file.
  • Adapters queue and stack inherits deque's problem with _Deque_base in a .cc file.

Other than that I wasn't able to use unordered associative containers as unordered_map and unordered_set defaulted destructors doesn't want to call non-constexpr destructor of internal _Hashtable common type (seems as a limitation of -fimplicit-constexpr mode), but by some looking at the code, there doesn't seem to be anything significant problem there.

There is no usage of reinterpret_cast in container library, only occurence is TR1 hash_table implementation which is irrelevant to this paper. No flat associative container is implemented yet.

Adapter priority_queue is usable without problem as it is based on vector πŸŽ‰πŸŽ‰

Impact on existing code

None on user code, this is purely an API extension as the previous API wasn't usable in constexpr, and all types are templates and must be available in headers already. Some implementation can have parts of the internals of containers hidden inside of .cpp files and these needs to be moved to headers / modules in order to make containers fully functional within constant evaluation.

Notes for wording changes

Mechanically mark all functionality in [container] constexpr with exception of node-handle::mapped() (which is subject of CWG-2514). Also make sure all iterator types are constexpr.

Proposed changes to wording

24 Containers library [containers]

24.2.5 Node handles [container.node]

24.2.5.1 Overview [container.node.overview]

A node handle is an object that accepts ownership of a single element from an associative container ([associative.reqmts]) or an unordered associative container ([unord.req]).
It may be used to transfer that ownership to another container with compatible nodes.
Containers with compatible nodes have the same node handle type.
Elements may be transferred in either direction between container types in the same row of Table 86.
Table 86: Container types with compatible nodes [tab:container.node.compat]
map<K, T, C1, A>
map<K, T, C2, A>
map<K, T, C1, A>
multimap<K, T, C2, A>
set<K, C1, A>
set<K, C2, A>
set<K, C1, A>
multiset<K, C2, A>
unordered_map<K, T, H1, E1, A>
unordered_map<K, T, H2, E2, A>
unordered_map<K, T, H1, E1, A>
unordered_multimap<K, T, H2, E2, A>
unordered_set<K, H1, E1, A>
unordered_set<K, H2, E2, A>
unordered_set<K, H1, E1, A>
unordered_multiset<K, H2, E2, A>
If a node handle is not empty, then it contains an allocator that is equal to the allocator of the container when the element was extracted.
If a node handle is empty, it contains no allocator.
Class node-handle is for exposition only.
If a user-defined specialization of pair exists for pair<const Key, T> or pair<Key, T>, where Key is the container's key_type and T is the container's mapped_type, the behavior of operations involving node handles is undefined.
template<unspecified> class node-handle { public: // These type declarations are described in [associative.reqmts] and [unord.req]. using value_type = see below; // not present for map containers using key_type = see below; // not present for set containers using mapped_type = see below; // not present for set containers using allocator_type = see below; private: using container_node_type = unspecified; // exposition only using ator_traits = allocator_traits<allocator_type>; // exposition only typename ator_traits::template rebind_traits<container_node_type>::pointer ptr_; // exposition only optional<allocator_type> alloc_; // exposition only public: // [container.node.cons], constructors, copy, and assignment constexpr node-handle() noexcept : ptr_(), alloc_() {} constexpr node-handle(node-handle&&) noexcept; constexpr node-handle& operator=(node-handle&&); // [container.node.dtor], destructor constexpr ~node-handle(); // [container.node.observers], observers constexpr value_type& value() const; // not present for map containers key_type& key() const; // not present for set containers constexpr mapped_type& mapped() const; // not present for set containers constexpr allocator_type get_allocator() const; constexpr explicit operator bool() const noexcept; constexpr bool empty() const noexcept; // [container.node.modifiers], modifiers constexpr void swap(node-handle&) noexcept(ator_traits::propagate_on_container_swap::value || ator_traits::is_always_equal::value); constexpr friend void swap(node-handle& x, node-handle& y) noexcept(noexcept(x.swap(y))) { x.swap(y); } };

24.2.5.2 Constructors, copy, and assignment [container.node.cons]

constexpr node-handle(node-handle&& nh) noexcept;
Effects: Constructs a node-handle object initializing ptr_ with nh.ptr_.
Move constructs alloc_ with nh.alloc_.
Assigns nullptr to nh.ptr_ and assigns nullopt to nh.alloc_.
constexpr node-handle& operator=(node-handle&& nh);
Preconditions: Either !alloc_, or ator_traits​::​propagate_on_container_move_assignment​::​value is true, or alloc_ == nh.alloc_.
Effects:
  • If ptr_ != nullptr, destroys the value_type subobject in the container_node_type object pointed to by ptr_ by calling ator_traits​::​destroy, then deallocates ptr_ by calling ator_traits​::​template rebind_traits<container_node_type>​::​deallocate.
  • Assigns nh.ptr_ to ptr_.
  • If !alloc_ or ator_traits​::​propagate_on_container_move_assignment​::​value is true, move assigns nh.alloc_ to alloc_.
  • Assigns nullptr to nh.ptr_ and assigns nullopt to nh.alloc_.
Returns: *this.
Throws: Nothing.

24.2.5.3 Destructor [container.node.dtor]

constexpr ~node-handle();
Effects: If ptr_ != nullptr, destroys the value_type subobject in the container_node_type object pointed to by ptr_ by calling ator_traits​::​destroy, then deallocates ptr_ by calling ator_traits​::​template rebind_traits<container_node_type>​::​deallocate.

24.2.5.4 Observers [container.node.observers]

constexpr value_type& value() const;
Preconditions: empty() == false.
Returns: A reference to the value_type subobject in the container_node_type object pointed to by ptr_.
Throws: Nothing.
key_type& key() const;
Preconditions: empty() == false.
Returns: A non-const reference to the key_type member of the value_type subobject in the container_node_type object pointed to by ptr_.
Throws: Nothing.
Remarks: Modifying the key through the returned reference is permitted.
constexpr mapped_type& mapped() const;
Preconditions: empty() == false.
Returns: A reference to the mapped_type member of the value_type subobject in the container_node_type object pointed to by ptr_.
Throws: Nothing.
constexpr allocator_type get_allocator() const;
Preconditions: empty() == false.
Returns: *alloc_.
Throws: Nothing.
constexpr explicit operator bool() const noexcept;
Returns: ptr_ != nullptr.
constexpr bool empty() const noexcept;
Returns: ptr_ == nullptr.

24.2.5.5 Modifiers [container.node.modifiers]

constexpr void swap(node-handle& nh) noexcept(ator_traits::propagate_on_container_swap::value || ator_traits::is_always_equal::value);
Preconditions: !alloc_, or !nh.alloc_, or ator_traits​::​propagate_on_container_swap​::​value is true, or alloc_ == nh.alloc_.
Effects: Calls swap(ptr_, nh.ptr_).
If !alloc_, or !nh.alloc_, or ator_traits​::​propagate_on_container_swap​::​value is true calls swap(alloc_, nh.alloc_).

24.3.3 Header <deque> synopsis [deque.syn]

#include <compare> // see [compare.syn] #include <initializer_list> // see [initializer.list.syn] namespace std { // [deque], class template deque template<class T, class Allocator = allocator<T>> class deque; template<class T, class Allocator> constexpr bool operator==(const deque<T, Allocator>& x, const deque<T, Allocator>& y); template<class T, class Allocator> constexpr synth-three-way-result<T> operator<=>(const deque<T, Allocator>& x, const deque<T, Allocator>& y); template<class T, class Allocator> constexpr void swap(deque<T, Allocator>& x, deque<T, Allocator>& y) noexcept(noexcept(x.swap(y))); // [deque.erasure], erasure template<class T, class Allocator, class U = T> constexpr typename deque<T, Allocator>::size_type erase(deque<T, Allocator>& c, const U& value); template<class T, class Allocator, class Predicate> constexpr typename deque<T, Allocator>::size_type erase_if(deque<T, Allocator>& c, Predicate pred); namespace pmr { template<class T> using deque = std::deque<T, polymorphic_allocator<T>>; } }

24.3.4 Header <forward_list> synopsis [forward.list.syn]

#include <compare> // see [compare.syn] #include <initializer_list> // see [initializer.list.syn] namespace std { // [forward.list], class template forward_list template<class T, class Allocator = allocator<T>> class forward_list; template<class T, class Allocator> constexpr bool operator==(const forward_list<T, Allocator>& x, const forward_list<T, Allocator>& y); template<class T, class Allocator> constexpr synth-three-way-result<T> operator<=>(const forward_list<T, Allocator>& x, const forward_list<T, Allocator>& y); template<class T, class Allocator> constexpr void swap(forward_list<T, Allocator>& x, forward_list<T, Allocator>& y) noexcept(noexcept(x.swap(y))); // [forward.list.erasure], erasure template<class T, class Allocator, class U = T> constexpr typename forward_list<T, Allocator>::size_type erase(forward_list<T, Allocator>& c, const U& value); template<class T, class Allocator, class Predicate> constexpr typename forward_list<T, Allocator>::size_type erase_if(forward_list<T, Allocator>& c, Predicate pred); namespace pmr { template<class T> using forward_list = std::forward_list<T, polymorphic_allocator<T>>; } }

24.3.5 Header <list> synopsis [list.syn]

#include <compare> // see [compare.syn] #include <initializer_list> // see [initializer.list.syn] namespace std { // [list], class template list template<class T, class Allocator = allocator<T>> class list; template<class T, class Allocator> constexpr bool operator==(const list<T, Allocator>& x, const list<T, Allocator>& y); template<class T, class Allocator> constexpr synth-three-way-result<T> operator<=>(const list<T, Allocator>& x, const list<T, Allocator>& y); template<class T, class Allocator> constexpr void swap(list<T, Allocator>& x, list<T, Allocator>& y) noexcept(noexcept(x.swap(y))); // [list.erasure], erasure template<class T, class Allocator, class U = T> constexpr typename list<T, Allocator>::size_type erase(list<T, Allocator>& c, const U& value); template<class T, class Allocator, class Predicate> constexpr typename list<T, Allocator>::size_type erase_if(list<T, Allocator>& c, Predicate pred); namespace pmr { template<class T> using list = std::list<T, polymorphic_allocator<T>>; } }

24.3.9 Class template deque [deque]

24.3.9.1 Overview [deque.overview]

A deque is a sequence container that supports random access iterators.
In addition, it supports constant time insert and erase operations at the beginning or the end; insert and erase in the middle take linear time.
That is, a deque is especially optimized for pushing and popping elements at the beginning and end.
Storage management is handled automatically.
A deque meets all of the requirements of a container ([container.reqmts]), of a reversible container ([container.rev.reqmts]), of an allocator-aware container ([container.alloc.reqmts]), and of a sequence container, including the optional sequence container requirements ([sequence.reqmts]).
Descriptions are provided here only for operations on deque that are not described in one of these tables or for operations where there is additional semantic information.
The types iterator and const_iterator meet the constexpr iterator requirements ([iterator.requirements.general]).
namespace std { template<class T, class Allocator = allocator<T>> class deque { public: // types using value_type = T; using allocator_type = Allocator; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; // [deque.cons], construct/copy/destroy constexpr deque() : deque(Allocator()) { } constexpr explicit deque(const Allocator&); constexpr explicit deque(size_type n, const Allocator& = Allocator()); constexpr deque(size_type n, const T& value, const Allocator& = Allocator()); template<class InputIterator> constexpr deque(InputIterator first, InputIterator last, const Allocator& = Allocator()); template<container-compatible-range<T> R> constexpr deque(from_range_t, R&& rg, const Allocator& = Allocator()); constexpr deque(const deque& x); constexpr deque(deque&&); constexpr deque(const deque&, const type_identity_t<Allocator>&); constexpr deque(deque&&, const type_identity_t<Allocator>&); constexpr deque(initializer_list<T>, const Allocator& = Allocator()); constexpr ~deque(); constexpr deque& operator=(const deque& x); constexpr deque& operator=(deque&& x) noexcept(allocator_traits<Allocator>::is_always_equal::value); constexpr deque& operator=(initializer_list<T>); template<class InputIterator> constexpr void assign(InputIterator first, InputIterator last); template<container-compatible-range<T> R> constexpr void assign_range(R&& rg); constexpr void assign(size_type n, const T& t); constexpr void assign(initializer_list<T>); constexpr allocator_type get_allocator() const noexcept; // iterators constexpr iterator begin() noexcept; constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; constexpr const_iterator end() const noexcept; constexpr reverse_iterator rbegin() noexcept; constexpr const_reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() noexcept; constexpr const_reverse_iterator rend() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; // [deque.capacity], capacity constexpr bool empty() const noexcept; constexpr size_type size() const noexcept; constexpr size_type max_size() const noexcept; constexpr void resize(size_type sz); constexpr void resize(size_type sz, const T& c); constexpr void shrink_to_fit(); // element access constexpr reference operator[](size_type n); constexpr const_reference operator[](size_type n) const; constexpr reference at(size_type n); constexpr const_reference at(size_type n) const; constexpr reference front(); constexpr const_reference front() const; constexpr reference back(); constexpr const_reference back() const; // [deque.modifiers], modifiers template<class... Args> constexpr reference emplace_front(Args&&... args); template<class... Args> constexpr reference emplace_back(Args&&... args); template<class... Args> constexpr iterator emplace(const_iterator position, Args&&... args); constexpr void push_front(const T& x); constexpr void push_front(T&& x); template<container-compatible-range<T> R> constexpr void prepend_range(R&& rg); constexpr void push_back(const T& x); constexpr void push_back(T&& x); template<container-compatible-range<T> R> constexpr void append_range(R&& rg); constexpr iterator insert(const_iterator position, const T& x); constexpr iterator insert(const_iterator position, T&& x); constexpr iterator insert(const_iterator position, size_type n, const T& x); template<class InputIterator> constexpr iterator insert(const_iterator position, InputIterator first, InputIterator last); template<container-compatible-range<T> R> constexpr iterator insert_range(const_iterator position, R&& rg); constexpr iterator insert(const_iterator position, initializer_list<T>); constexpr void pop_front(); constexpr void pop_back(); constexpr iterator erase(const_iterator position); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void swap(deque&) noexcept(allocator_traits<Allocator>::is_always_equal::value); constexpr void clear() noexcept; }; template<class InputIterator, class Allocator = allocator<iter-value-type<InputIterator>>> deque(InputIterator, InputIterator, Allocator = Allocator()) -> deque<iter-value-type<InputIterator>, Allocator>; template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>> deque(from_range_t, R&&, Allocator = Allocator()) -> deque<ranges::range_value_t<R>, Allocator>; }

24.3.9.2 Constructors, copy, and assignment [deque.cons]

constexpr explicit deque(const Allocator&);
Effects: Constructs an empty deque, using the specified allocator.
Complexity: Constant.
constexpr explicit deque(size_type n, const Allocator& = Allocator());
Preconditions: T is Cpp17DefaultInsertable into deque.
Effects: Constructs a deque with n default-inserted elements using the specified allocator.
Complexity: Linear in n.
constexpr deque(size_type n, const T& value, const Allocator& = Allocator());
Preconditions: T is Cpp17CopyInsertable into deque.
Effects: Constructs a deque with n copies of value, using the specified allocator.
Complexity: Linear in n.
template<class InputIterator> constexpr deque(InputIterator first, InputIterator last, const Allocator& = Allocator());
Effects: Constructs a deque equal to the range [first, last), using the specified allocator.
Complexity: Linear in distance(first, last).
template<container-compatible-range<T> R> constexpr deque(from_range_t, R&& rg, const Allocator& = Allocator());
Effects: Constructs a deque with the elements of the range rg, using the specified allocator.
Complexity: Linear in ranges​::​distance(rg).

24.3.9.3 Capacity [deque.capacity]

constexpr void resize(size_type sz);
Preconditions: T is Cpp17MoveInsertable and Cpp17DefaultInsertable into deque.
Effects: If sz < size(), erases the last size() - sz elements from the sequence.
Otherwise, appends sz - size() default-inserted elements to the sequence.
constexpr void resize(size_type sz, const T& c);
Preconditions: T is Cpp17CopyInsertable into deque.
Effects: If sz < size(), erases the last size() - sz elements from the sequence.
Otherwise, appends sz - size() copies of c to the sequence.
constexpr void shrink_to_fit();
Preconditions: T is Cpp17MoveInsertable into deque.
Effects: shrink_to_fit is a non-binding request to reduce memory use but does not change the size of the sequence.
[Note 1: 
The request is non-binding to allow latitude for implementation-specific optimizations.
β€” end note]
If the size is equal to the old capacity, or if an exception is thrown other than by the move constructor of a non-Cpp17CopyInsertable T, then there are no effects.
Complexity: If the size is not equal to the old capacity, linear in the size of the sequence; otherwise constant.
Remarks: If the size is not equal to the old capacity, then invalidates all the references, pointers, and iterators referring to the elements in the sequence, as well as the past-the-end iterator.

24.3.9.4 Modifiers [deque.modifiers]

constexpr iterator insert(const_iterator position, const T& x); constexpr iterator insert(const_iterator position, T&& x); constexpr iterator insert(const_iterator position, size_type n, const T& x); template<class InputIterator> constexpr iterator insert(const_iterator position, InputIterator first, InputIterator last); template<container-compatible-range<T> R> constexpr iterator insert_range(const_iterator position, R&& rg); constexpr iterator insert(const_iterator position, initializer_list<T>); template<class... Args> constexpr reference emplace_front(Args&&... args); template<class... Args> constexpr reference emplace_back(Args&&... args); template<class... Args> constexpr iterator emplace(const_iterator position, Args&&... args); constexpr void push_front(const T& x); constexpr void push_front(T&& x); template<container-compatible-range<T> R> constexpr void prepend_range(R&& rg); constexpr void push_back(const T& x); constexpr void push_back(T&& x); template<container-compatible-range<T> R> constexpr void append_range(R&& rg);
Effects: An insertion in the middle of the deque invalidates all the iterators and references to elements of the deque.
An insertion at either end of the deque invalidates all the iterators to the deque, but has no effect on the validity of references to elements of the deque.
Complexity: The complexity is linear in the number of elements inserted plus the lesser of the distances to the beginning and end of the deque.
Inserting a single element at either the beginning or end of a deque always takes constant time and causes a single call to a constructor of T.
Remarks: If an exception is thrown other than by the copy constructor, move constructor, assignment operator, or move assignment operator of T there are no effects.
If an exception is thrown while inserting a single element at either end, there are no effects.
Otherwise, if an exception is thrown by the move constructor of a non-Cpp17CopyInsertable T, the effects are unspecified.
constexpr iterator erase(const_iterator position); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void pop_front(); constexpr void pop_back();
Effects: An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements.
An erase operation that erases the first element of a deque but not the last element invalidates only iterators and references to the erased elements.
An erase operation that erases neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque.
[Note 1: 
pop_front and pop_back are erase operations.
β€” end note]
Throws: Nothing unless an exception is thrown by the assignment operator of T.
Complexity: The number of calls to the destructor of T is the same as the number of elements erased, but the number of calls to the assignment operator of T is no more than the lesser of the number of elements before the erased elements and the number of elements after the erased elements.

24.3.9.5 Erasure [deque.erasure]

template<class T, class Allocator, class U = T> constexpr typename deque<T, Allocator>::size_type erase(deque<T, Allocator>& c, const U& value);
Effects: Equivalent to: auto it = remove(c.begin(), c.end(), value); auto r = distance(it, c.end()); c.erase(it, c.end()); return r;
template<class T, class Allocator, class Predicate> constexpr typename deque<T, Allocator>::size_type erase_if(deque<T, Allocator>& c, Predicate pred);
Effects: Equivalent to: auto it = remove_if(c.begin(), c.end(), pred); auto r = distance(it, c.end()); c.erase(it, c.end()); return r;

24.3.10 Class template forward_list [forward.list]

24.3.10.1 Overview [forward.list.overview]

A forward_list is a container that supports forward iterators and allows constant time insert and erase operations anywhere within the sequence, with storage management handled automatically.
Fast random access to list elements is not supported.
[Note 1: 
It is intended that forward_list have zero space or time overhead relative to a hand-written C-style singly linked list.
Features that would conflict with that goal have been omitted.
β€” end note]
A forward_list meets all of the requirements of a container ([container.reqmts]), except that the size() member function is not provided and operator== has linear complexity, A forward_list also meets all of the requirements for an allocator-aware container ([container.alloc.reqmts]).
In addition, a forward_list provides the assign member functions and several of the optional sequence container requirements ([sequence.reqmts]).
Descriptions are provided here only for operations on forward_list that are not described in that table or for operations where there is additional semantic information.
The types iterator and const_iterator meet the constexpr iterator requirements ([iterator.requirements.general]).
[Note 2: 
Modifying any list requires access to the element preceding the first element of interest, but in a forward_list there is no constant-time way to access a preceding element.
For this reason, erase_after and splice_after take fully-open ranges, not semi-open ranges.
β€” end note]
namespace std { template<class T, class Allocator = allocator<T>> class forward_list { public: // types using value_type = T; using allocator_type = Allocator; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] // [forward.list.cons], construct/copy/destroy constexpr forward_list() : forward_list(Allocator()) { } constexpr explicit forward_list(const Allocator&); constexpr explicit forward_list(size_type n, const Allocator& = Allocator()); constexpr forward_list(size_type n, const T& value, const Allocator& = Allocator()); template<class InputIterator> constexpr forward_list(InputIterator first, InputIterator last, const Allocator& = Allocator()); template<container-compatible-range<T> R> constexpr forward_list(from_range_t, R&& rg, const Allocator& = Allocator()); constexpr forward_list(const forward_list& x); constexpr forward_list(forward_list&& x); constexpr forward_list(const forward_list& x, const type_identity_t<Allocator>&); constexpr forward_list(forward_list&& x, const type_identity_t<Allocator>&); constexpr forward_list(initializer_list<T>, const Allocator& = Allocator()); constexpr ~forward_list(); constexpr forward_list& operator=(const forward_list& x); constexpr forward_list& operator=(forward_list&& x) noexcept(allocator_traits<Allocator>::is_always_equal::value); constexpr forward_list& operator=(initializer_list<T>); template<class InputIterator> constexpr void assign(InputIterator first, InputIterator last); template<container-compatible-range<T> R> constexpr void assign_range(R&& rg); constexpr void assign(size_type n, const T& t); constexpr void assign(initializer_list<T>); constexpr allocator_type get_allocator() const noexcept; // [forward.list.iter], iterators constexpr iterator before_begin() noexcept; constexpr const_iterator before_begin() const noexcept; constexpr iterator begin() noexcept; constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; constexpr const_iterator end() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cbefore_begin() const noexcept; constexpr const_iterator cend() const noexcept; // capacity constexpr bool empty() const noexcept; constexpr size_type max_size() const noexcept; // [forward.list.access], element access constexpr reference front(); constexpr const_reference front() const; // [forward.list.modifiers], modifiers template<class... Args> constexpr reference emplace_front(Args&&... args); constexpr void push_front(const T& x); constexpr void push_front(T&& x); template<container-compatible-range<T> R> constexpr void prepend_range(R&& rg); constexpr void pop_front(); template<class... Args> constexpr iterator emplace_after(const_iterator position, Args&&... args); constexpr iterator insert_after(const_iterator position, const T& x); constexpr iterator insert_after(const_iterator position, T&& x); constexpr iterator insert_after(const_iterator position, size_type n, const T& x); template<class InputIterator> constexpr iterator insert_after(const_iterator position, InputIterator first, InputIterator last); constexpr iterator insert_after(const_iterator position, initializer_list<T> il); template<container-compatible-range<T> R> constexpr iterator insert_range_after(const_iterator position, R&& rg); constexpr iterator erase_after(const_iterator position); constexpr iterator erase_after(const_iterator position, const_iterator last); constexpr void swap(forward_list&) noexcept(allocator_traits<Allocator>::is_always_equal::value); constexpr void resize(size_type sz); constexpr void resize(size_type sz, const value_type& c); constexpr void clear() noexcept; // [forward.list.ops], forward_list operations constexpr void splice_after(const_iterator position, forward_list& x); constexpr void splice_after(const_iterator position, forward_list&& x); constexpr void splice_after(const_iterator position, forward_list& x, const_iterator i); constexpr void splice_after(const_iterator position, forward_list&& x, const_iterator i); constexpr void splice_after(const_iterator position, forward_list& x, const_iterator first, const_iterator last); constexpr void splice_after(const_iterator position, forward_list&& x, const_iterator first, const_iterator last); constexpr size_type remove(const T& value); template<class Predicate> constexpr size_type remove_if(Predicate pred); constexpr size_type unique(); template<class BinaryPredicate> constexpr size_type unique(BinaryPredicate binary_pred); constexpr void merge(forward_list& x); constexpr void merge(forward_list&& x); template<class Compare> constexpr void merge(forward_list& x, Compare comp); template<class Compare> constexpr void merge(forward_list&& x, Compare comp); constexpr void sort(); template<class Compare> constexpr void sort(Compare comp); constexpr void reverse() noexcept; }; template<class InputIterator, class Allocator = allocator<iter-value-type<InputIterator>>> forward_list(InputIterator, InputIterator, Allocator = Allocator()) -> forward_list<iter-value-type<InputIterator>, Allocator>; template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>> forward_list(from_range_t, R&&, Allocator = Allocator()) -> forward_list<ranges::range_value_t<R>, Allocator>; }
An incomplete type T may be used when instantiating forward_list if the allocator meets the allocator completeness requirements.
T shall be complete before any member of the resulting specialization of forward_list is referenced.

24.3.10.2 Constructors, copy, and assignment [forward.list.cons]

constexpr explicit forward_list(const Allocator&);
Effects: Constructs an empty forward_list object using the specified allocator.
Complexity: Constant.
constexpr explicit forward_list(size_type n, const Allocator& = Allocator());
Preconditions: T is Cpp17DefaultInsertable into forward_list.
Effects: Constructs a forward_list object with n default-inserted elements using the specified allocator.
Complexity: Linear in n.
constexpr forward_list(size_type n, const T& value, const Allocator& = Allocator());
Preconditions: T is Cpp17CopyInsertable into forward_list.
Effects: Constructs a forward_list object with n copies of value using the specified allocator.
Complexity: Linear in n.
template<class InputIterator> constexpr forward_list(InputIterator first, InputIterator last, const Allocator& = Allocator());
Effects: Constructs a forward_list object equal to the range [first, last).
Complexity: Linear in distance(first, last).
template<container-compatible-range<T> R> constexpr forward_list(from_range_t, R&& rg, const Allocator& = Allocator());
Effects: Constructs a forward_list object with the elements of the range rg.
Complexity: Linear in ranges​::​distance(rg).

24.3.10.3 Iterators [forward.list.iter]

constexpr iterator before_begin() noexcept; constexpr const_iterator before_begin() const noexcept; constexpr const_iterator cbefore_begin() const noexcept;
Effects: cbefore_begin() is equivalent to const_cast<forward_list const&>(*this).before_begin().
Returns: A non-dereferenceable iterator that, when incremented, is equal to the iterator returned by begin().
Remarks: before_begin() == end() shall equal false.

24.3.10.4 Element access [forward.list.access]

constexpr reference front(); constexpr const_reference front() const;
Returns: *begin()

24.3.10.5 Modifiers [forward.list.modifiers]

None of the overloads of insert_after shall affect the validity of iterators and references, and erase_after shall invalidate only iterators and references to the erased elements.
If an exception is thrown during insert_after there shall be no effect.
Inserting n elements into a forward_list is linear in n, and the number of calls to the copy or move constructor of T is exactly equal to n.
Erasing n elements from a forward_list is linear in n and the number of calls to the destructor of type T is exactly equal to n.
template<class... Args> constexpr reference emplace_front(Args&&... args);
Effects: Inserts an object of type value_type constructed with value_type(std​::​forward<Args>(​args)...) at the beginning of the list.
constexpr void push_front(const T& x); constexpr void push_front(T&& x);
Effects: Inserts a copy of x at the beginning of the list.
template<container-compatible-range<T> R> constexpr void prepend_range(R&& rg);
Effects: Inserts a copy of each element of rg at the beginning of the list.
[Note 1: 
The order of elements is not reversed.
β€” end note]
constexpr void pop_front();
Effects: As if by erase_after(before_begin()).
constexpr iterator insert_after(const_iterator position, const T& x);
Preconditions: T is Cpp17CopyInsertable into forward_list.
position is before_begin() or is a dereferenceable iterator in the range [begin(), end()).
Effects: Inserts a copy of x after position.
Returns: An iterator pointing to the copy of x.
constexpr iterator insert_after(const_iterator position, T&& x);
Preconditions: T is Cpp17MoveInsertable into forward_list.
position is before_begin() or is a dereferenceable iterator in the range [begin(), end()).
Effects: Inserts a copy of x after position.
Returns: An iterator pointing to the copy of x.
constexpr iterator insert_after(const_iterator position, size_type n, const T& x);
Preconditions: T is Cpp17CopyInsertable into forward_list.
position is before_begin() or is a dereferenceable iterator in the range [begin(), end()).
Effects: Inserts n copies of x after position.
Returns: An iterator pointing to the last inserted copy of x, or position if n == 0 is true.
template<class InputIterator> constexpr iterator insert_after(const_iterator position, InputIterator first, InputIterator last);
Preconditions: T is Cpp17EmplaceConstructible into forward_list from *first.
position is before_begin() or is a dereferenceable iterator in the range [begin(), end()).
Neither first nor last are iterators in *this.
Effects: Inserts copies of elements in [first, last) after position.
Returns: An iterator pointing to the last inserted element, or position if first == last is true.
template<container-compatible-range<T> R> constexpr iterator insert_range_after(const_iterator position, R&& rg);
Preconditions: T is Cpp17EmplaceConstructible into forward_list from *ranges​::​begin(rg).
position is before_begin() or is a dereferenceable iterator in the range [begin(), end()).
rg and *this do not overlap.
Effects: Inserts copies of elements in the range rg after position.
Returns: An iterator pointing to the last inserted element, or position if rg is empty.
constexpr iterator insert_after(const_iterator position, initializer_list<T> il);
Effects: Equivalent to: return insert_after(position, il.begin(), il.end());
template<class... Args> constexpr iterator emplace_after(const_iterator position, Args&&... args);
Preconditions: T is Cpp17EmplaceConstructible into forward_list from std​::​forward<Args>(args)....
position is before_begin() or is a dereferenceable iterator in the range [begin(), end()).
Effects: Inserts an object of type value_type direct-non-list-initialized with std​::​forward<Args>(args)... after position.
Returns: An iterator pointing to the new object.
constexpr iterator erase_after(const_iterator position);
Preconditions: The iterator following position is dereferenceable.
Effects: Erases the element pointed to by the iterator following position.
Returns: An iterator pointing to the element following the one that was erased, or end() if no such element exists.
Throws: Nothing.
constexpr iterator erase_after(const_iterator position, const_iterator last);
Preconditions: All iterators in the range (position, last) are dereferenceable.
Effects: Erases the elements in the range (position, last).
Returns: last.
Throws: Nothing.
constexpr void resize(size_type sz);
Preconditions: T is Cpp17DefaultInsertable into forward_list.
Effects: If sz < distance(begin(), end()), erases the last distance(begin(), end()) - sz elements from the list.
Otherwise, inserts sz - distance(begin(), end()) default-inserted elements at the end of the list.
constexpr void resize(size_type sz, const value_type& c);
Preconditions: T is Cpp17CopyInsertable into forward_list.
Effects: If sz < distance(begin(), end()), erases the last distance(begin(), end()) - sz elements from the list.
Otherwise, inserts sz - distance(begin(), end()) copies of c at the end of the list.
constexpr void clear() noexcept;
Effects: Erases all elements in the range [begin(), end()).
Remarks: Does not invalidate past-the-end iterators.

24.3.10.6 Operations [forward.list.ops]

In this subclause, arguments for a template parameter named Predicate or BinaryPredicate shall meet the corresponding requirements in [algorithms.requirements].
The semantics of i + n, where i is an iterator into the list and n is an integer, are the same as those of next(i, n).
The expression i - n, where i is an iterator into the list and n is an integer, means an iterator j such that j + n == i is true.
For merge and sort, the definitions and requirements in [alg.sorting] apply.
constexpr void splice_after(const_iterator position, forward_list& x); constexpr void splice_after(const_iterator position, forward_list&& x);
Preconditions: position is before_begin() or is a dereferenceable iterator in the range [begin(), end()).
get_allocator() == x.get_allocator() is true.
addressof(x) != this is true.
Effects: Inserts the contents of x after position, and x becomes empty.
Pointers and references to the moved elements of x now refer to those same elements but as members of *this.
Iterators referring to the moved elements will continue to refer to their elements, but they now behave as iterators into *this, not into x.
Throws: Nothing.
Complexity:
constexpr void splice_after(const_iterator position, forward_list& x, const_iterator i); constexpr void splice_after(const_iterator position, forward_list&& x, const_iterator i);
Preconditions: position is before_begin() or is a dereferenceable iterator in the range [begin(), end()).
The iterator following i is a dereferenceable iterator in x.
get_allocator() == x.get_allocator() is true.
Effects: Inserts the element following i into *this, following position, and removes it from x.
The result is unchanged if position == i or position == ++i.
Pointers and references to *++i continue to refer to the same element but as a member of *this.
Iterators to *++i continue to refer to the same element, but now behave as iterators into *this, not into x.
Throws: Nothing.
Complexity:
constexpr void splice_after(const_iterator position, forward_list& x, const_iterator first, const_iterator last); constexpr void splice_after(const_iterator position, forward_list&& x, const_iterator first, const_iterator last);
Preconditions: position is before_begin() or is a dereferenceable iterator in the range [begin(), end()).
(first, last) is a valid range in x, and all iterators in the range (first, last) are dereferenceable.
position is not an iterator in the range (first, last).
get_allocator() == x.get_allocator() is true.
Effects: Inserts elements in the range (first, last) after position and removes the elements from x.
Pointers and references to the moved elements of x now refer to those same elements but as members of *this.
Iterators referring to the moved elements will continue to refer to their elements, but they now behave as iterators into *this, not into x.
Complexity:
constexpr size_type remove(const T& value); template<class Predicate> constexpr size_type remove_if(Predicate pred);
Effects: Erases all the elements in the list referred to by a list iterator i for which the following conditions hold: *i == value (for remove()), pred(*i) is true (for remove_if()).
Invalidates only the iterators and references to the erased elements.
Returns: The number of elements erased.
Throws: Nothing unless an exception is thrown by the equality comparison or the predicate.
Complexity: Exactly distance(begin(), end()) applications of the corresponding predicate.
Remarks: Stable.
constexpr size_type unique(); template<class BinaryPredicate> constexpr size_type unique(BinaryPredicate binary_pred);
Let binary_pred be equal_to<>{} for the first overload.
Preconditions: binary_pred is an equivalence relation.
Effects: Erases all but the first element from every consecutive group of equivalent elements.
That is, for a nonempty list, erases all elements referred to by the iterator i in the range [begin() + 1, end()) for which binary_pred(*i, *(i - 1)) is true.
Invalidates only the iterators and references to the erased elements.
Returns: The number of elements erased.
Throws: Nothing unless an exception is thrown by the predicate.
Complexity: If empty() is false, exactly distance(begin(), end()) - 1 applications of the corresponding predicate, otherwise no applications of the predicate.
constexpr void merge(forward_list& x); constexpr void merge(forward_list&& x); template<class Compare> constexpr void merge(forward_list& x, Compare comp); template<class Compare> constexpr void merge(forward_list&& x, Compare comp);
Let comp be less<> for the first two overloads.
Preconditions: *this and x are both sorted with respect to the comparator comp, and get_allocator() == x.get_allocator() is true.
Effects: If addressof(x) == this, there are no effects.
Otherwise, merges the two sorted ranges [begin(), end()) and [x.begin(), x.end()).
The result is a range that is sorted with respect to the comparator comp.
Pointers and references to the moved elements of x now refer to those same elements but as members of *this.
Iterators referring to the moved elements will continue to refer to their elements, but they now behave as iterators into *this, not into x.
Complexity: At most distance(begin(), end()) + distance(x.begin(), x.end()) - 1 comparisons if addressof(x) != this; otherwise, no comparisons are performed.
Remarks: Stable ([algorithm.stable]).
If addressof(x) != this, x is empty after the merge.
No elements are copied by this operation.
If an exception is thrown other than by a comparison, there are no effects.
constexpr void sort(); template<class Compare> constexpr void sort(Compare comp);
Effects: Sorts the list according to the operator< or the comp function object.
If an exception is thrown, the order of the elements in *this is unspecified.
Does not affect the validity of iterators and references.
Complexity: Approximately comparisons, where N is distance(begin(), end()).
Remarks: Stable.
constexpr void reverse() noexcept;
Effects: Reverses the order of the elements in the list.
Does not affect the validity of iterators and references.
Complexity: Linear time.

24.3.10.7 Erasure [forward.list.erasure]

template<class T, class Allocator, class U = T> constexpr typename forward_list<T, Allocator>::size_type erase(forward_list<T, Allocator>& c, const U& value);
Effects: Equivalent to: return erase_if(c, [&](auto& elem) { return elem == value; });
template<class T, class Allocator, class Predicate> constexpr typename forward_list<T, Allocator>::size_type erase_if(forward_list<T, Allocator>& c, Predicate pred);
Effects: Equivalent to: return c.remove_if(pred);

24.3.11 Class template list [list]

24.3.11.1 Overview [list.overview]

A list is a sequence container that supports bidirectional iterators and allows constant time insert and erase operations anywhere within the sequence, with storage management handled automatically.
Unlike vectors and deques, fast random access to list elements is not supported, but many algorithms only need sequential access anyway.
A list meets all of the requirements of a container ([container.reqmts]), of a reversible container ([container.rev.reqmts]), of an allocator-aware container ([container.alloc.reqmts]), and of a sequence container, including most of the optional sequence container requirements ([sequence.reqmts]).
The exceptions are the operator[] and at member functions, which are not provided.204
Descriptions are provided here only for operations on list that are not described in one of these tables or for operations where there is additional semantic information.
The types iterator and const_iterator meet the constexpr iterator requirements ([iterator.requirements.general]).
namespace std { template<class T, class Allocator = allocator<T>> class list { public: // types using value_type = T; using allocator_type = Allocator; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; // [list.cons], construct/copy/destroy constexpr list() : list(Allocator()) { } constexpr explicit list(const Allocator&); constexpr explicit list(size_type n, const Allocator& = Allocator()); constexpr list(size_type n, const T& value, const Allocator& = Allocator()); template<class InputIterator> constexpr list(InputIterator first, InputIterator last, const Allocator& = Allocator()); template<container-compatible-range<T> R> constexpr list(from_range_t, R&& rg, const Allocator& = Allocator()); constexpr list(const list& x); constexpr list(list&& x); constexpr list(const list&, const type_identity_t<Allocator>&); constexpr list(list&&, const type_identity_t<Allocator>&); constexpr list(initializer_list<T>, const Allocator& = Allocator()); constexpr ~list(); constexpr list& operator=(const list& x); constexpr list& operator=(list&& x) noexcept(allocator_traits<Allocator>::is_always_equal::value); constexpr list& operator=(initializer_list<T>); template<class InputIterator> constexpr void assign(InputIterator first, InputIterator last); template<container-compatible-range<T> R> constexpr void assign_range(R&& rg); constexpr void assign(size_type n, const T& t); constexpr void assign(initializer_list<T>); constexpr allocator_type get_allocator() const noexcept; // iterators constexpr iterator begin() noexcept; constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; constexpr const_iterator end() const noexcept; constexpr reverse_iterator rbegin() noexcept; constexpr const_reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() noexcept; constexpr const_reverse_iterator rend() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; // [list.capacity], capacity constexpr bool empty() const noexcept; constexpr size_type size() const noexcept; constexpr size_type max_size() const noexcept; constexpr void resize(size_type sz); constexpr void resize(size_type sz, const T& c); // element access constexpr reference front(); constexpr const_reference front() const; constexpr reference back(); constexpr const_reference back() const; // [list.modifiers], modifiers template<class... Args> constexpr reference emplace_front(Args&&... args); template<class... Args> constexpr reference emplace_back(Args&&... args); constexpr void push_front(const T& x); constexpr void push_front(T&& x); template<container-compatible-range<T> R> constexpr void prepend_range(R&& rg); constexpr void pop_front(); constexpr void push_back(const T& x); constexpr void push_back(T&& x); template<container-compatible-range<T> R> constexpr void append_range(R&& rg); constexpr void pop_back(); template<class... Args> iterator emplace(const_iterator position, Args&&... args); constexpr iterator insert(const_iterator position, const T& x); constexpr iterator insert(const_iterator position, T&& x); constexpr iterator insert(const_iterator position, size_type n, const T& x); template<class InputIterator> constexpr iterator insert(const_iterator position, InputIterator first, InputIterator last); template<container-compatible-range<T> R> constexpr iterator insert_range(const_iterator position, R&& rg); constexpr iterator insert(const_iterator position, initializer_list<T> il); constexpr iterator erase(const_iterator position); constexpr iterator erase(const_iterator position, const_iterator last); constexpr void swap(list&) noexcept(allocator_traits<Allocator>::is_always_equal::value); constexpr void clear() noexcept; // [list.ops], list operations constexpr void splice(const_iterator position, list& x); constexpr void splice(const_iterator position, list&& x); constexpr void splice(const_iterator position, list& x, const_iterator i); constexpr void splice(const_iterator position, list&& x, const_iterator i); constexpr void splice(const_iterator position, list& x, const_iterator first, const_iterator last); constexpr void splice(const_iterator position, list&& x, const_iterator first, const_iterator last); constexpr size_type remove(const T& value); template<class Predicate> constexpr size_type remove_if(Predicate pred); constexpr size_type unique(); template<class BinaryPredicate> constexpr size_type unique(BinaryPredicate binary_pred); constexpr void merge(list& x); constexpr void merge(list&& x); template<class Compare> constexpr void merge(list& x, Compare comp); template<class Compare> constexpr void merge(list&& x, Compare comp); constexpr void sort(); template<class Compare> constexpr void sort(Compare comp); constexpr void reverse() noexcept; }; template<class InputIterator, class Allocator = allocator<iter-value-type<InputIterator>>> list(InputIterator, InputIterator, Allocator = Allocator()) -> list<iter-value-type<InputIterator>, Allocator>; template<ranges::input_range R, class Allocator = allocator<ranges::range_value_t<R>>> list(from_range_t, R&&, Allocator = Allocator()) -> list<ranges::range_value_t<R>, Allocator>; }
An incomplete type T may be used when instantiating list if the allocator meets the allocator completeness requirements.
T shall be complete before any member of the resulting specialization of list is referenced.
204)204)
These member functions are only provided by containers whose iterators are random access iterators.

24.3.11.2 Constructors, copy, and assignment [list.cons]

constexpr explicit list(const Allocator&);
Effects: Constructs an empty list, using the specified allocator.
Complexity: Constant.
constexpr explicit list(size_type n, const Allocator& = Allocator());
Preconditions: T is Cpp17DefaultInsertable into list.
Effects: Constructs a list with n default-inserted elements using the specified allocator.
Complexity: Linear in n.
constexpr list(size_type n, const T& value, const Allocator& = Allocator());
Preconditions: T is Cpp17CopyInsertable into list.
Effects: Constructs a list with n copies of value, using the specified allocator.
Complexity: Linear in n.
template<class InputIterator> constexpr list(InputIterator first, InputIterator last, const Allocator& = Allocator());
Effects: Constructs a list equal to the range [first, last).
Complexity: Linear in distance(first, last).
template<container-compatible-range<T> R> constexpr list(from_range_t, R&& rg, const Allocator& = Allocator());
Effects: Constructs a list object with the elements of the range rg.
Complexity: Linear in ranges​::​distance(rg).

24.3.11.3 Capacity [list.capacity]

constexpr void resize(size_type sz);
Preconditions: T is Cpp17DefaultInsertable into list.
Effects: If size() < sz, appends sz - size() default-inserted elements to the sequence.
If sz <= size(), equivalent to: list<T>::iterator it = begin(); advance(it, sz); erase(it, end());
constexpr void resize(size_type sz, const T& c);
Preconditions: T is Cpp17CopyInsertable into list.
Effects: As if by: if (sz > size()) insert(end(), sz-size(), c); else if (sz < size()) { iterator i = begin(); advance(i, sz); erase(i, end()); } else ; // do nothing

24.3.11.4 Modifiers [list.modifiers]

constexpr iterator insert(const_iterator position, const T& x); constexpr iterator insert(const_iterator position, T&& x); constexpr iterator insert(const_iterator position, size_type n, const T& x); template<class InputIterator> constexpr iterator insert(const_iterator position, InputIterator first, InputIterator last); template<container-compatible-range<T> R> constexpr iterator insert_range(const_iterator position, R&& rg); constexpr iterator insert(const_iterator position, initializer_list<T>); template<class... Args> constexpr reference emplace_front(Args&&... args); template<class... Args> constexpr reference emplace_back(Args&&... args); template<class... Args> constexpr iterator emplace(const_iterator position, Args&&... args); constexpr void push_front(const T& x); constexpr void push_front(T&& x); template<container-compatible-range<T> R> constexpr void prepend_range(R&& rg); constexpr void push_back(const T& x); constexpr void push_back(T&& x); template<container-compatible-range<T> R> constexpr void append_range(R&& rg);
Complexity: Insertion of a single element into a list takes constant time and exactly one call to a constructor of T.
Insertion of multiple elements into a list is linear in the number of elements inserted, and the number of calls to the copy constructor or move constructor of T is exactly equal to the number of elements inserted.
Remarks: Does not affect the validity of iterators and references.
If an exception is thrown there are no effects.
constexpr iterator erase(const_iterator position); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void pop_front(); constexpr void pop_back(); constexpr void clear() noexcept;
Effects: Invalidates only the iterators and references to the erased elements.
Throws: Nothing.
Complexity: Erasing a single element is a constant time operation with a single call to the destructor of T.
Erasing a range in a list is linear time in the size of the range and the number of calls to the destructor of type T is exactly equal to the size of the range.

24.3.11.5 Operations [list.ops]

Since lists allow fast insertion and erasing from the middle of a list, certain operations are provided specifically for them.205
In this subclause, arguments for a template parameter named Predicate or BinaryPredicate shall meet the corresponding requirements in [algorithms.requirements].
The semantics of i + n and i - n, where i is an iterator into the list and n is an integer, are the same as those of next(i, n) and prev(i, n), respectively.
For merge and sort, the definitions and requirements in [alg.sorting] apply.
list provides three splice operations that destructively move elements from one list to another.
The behavior of splice operations is undefined if get_allocator() != x.get_allocator().
constexpr void splice(const_iterator position, list& x); constexpr void splice(const_iterator position, list&& x);
Preconditions: addressof(x) != this is true.
Effects: Inserts the contents of x before position and x becomes empty.
Pointers and references to the moved elements of x now refer to those same elements but as members of *this.
Iterators referring to the moved elements will continue to refer to their elements, but they now behave as iterators into *this, not into x.
Throws: Nothing.
Complexity: Constant time.
constexpr void splice(const_iterator position, list& x, const_iterator i); constexpr void splice(const_iterator position, list&& x, const_iterator i);
Preconditions: i is a valid dereferenceable iterator of x.
Effects: Inserts an element pointed to by i from list x before position and removes the element from x.
The result is unchanged if position == i or position == ++i.
Pointers and references to *i continue to refer to this same element but as a member of *this.
Iterators to *i (including i itself) continue to refer to the same element, but now behave as iterators into *this, not into x.
Throws: Nothing.
Complexity: Constant time.
constexpr void splice(const_iterator position, list& x, const_iterator first, const_iterator last); constexpr void splice(const_iterator position, list&& x, const_iterator first, const_iterator last);
Preconditions: [first, last) is a valid range in x.
position is not an iterator in the range [first, last).
Effects: Inserts elements in the range [first, last) before position and removes the elements from x.
Pointers and references to the moved elements of x now refer to those same elements but as members of *this.
Iterators referring to the moved elements will continue to refer to their elements, but they now behave as iterators into *this, not into x.
Throws: Nothing.
Complexity: Constant time if addressof(x) == this; otherwise, linear time.
constexpr size_type remove(const T& value); template<class Predicate> constexpr size_type remove_if(Predicate pred);
Effects: Erases all the elements in the list referred to by a list iterator i for which the following conditions hold: *i == value, pred(*i) != false.
Invalidates only the iterators and references to the erased elements.
Returns: The number of elements erased.
Throws: Nothing unless an exception is thrown by *i == value or pred(*i) != false.
Complexity: Exactly size() applications of the corresponding predicate.
Remarks: Stable.
constexpr size_type unique(); template<class BinaryPredicate> constexpr size_type unique(BinaryPredicate binary_pred);
Let binary_pred be equal_to<>{} for the first overload.
Preconditions: binary_pred is an equivalence relation.
Effects: Erases all but the first element from every consecutive group of equivalent elements.
That is, for a nonempty list, erases all elements referred to by the iterator i in the range [begin() + 1, end()) for which binary_pred(*i, *(i - 1)) is true.
Invalidates only the iterators and references to the erased elements.
Returns: The number of elements erased.
Throws: Nothing unless an exception is thrown by the predicate.
Complexity: If empty() is false, exactly size() - 1 applications of the corresponding predicate, otherwise no applications of the predicate.
constexpr void merge(list& x); constexpr void merge(list&& x); template<class Compare> constexpr void merge(list& x, Compare comp); template<class Compare> constexpr void merge(list&& x, Compare comp);
Let comp be less<> for the first two overloads.
Preconditions: *this and x are both sorted with respect to the comparator comp, and get_allocator() == x.get_allocator() is true.
Effects: If addressof(x) == this, there are no effects.
Otherwise, merges the two sorted ranges [begin(), end()) and [x.begin(), x.end()).
The result is a range that is sorted with respect to the comparator comp.
Pointers and references to the moved elements of x now refer to those same elements but as members of *this.
Iterators referring to the moved elements will continue to refer to their elements, but they now behave as iterators into *this, not into x.
Complexity: At most size() + x.size() - 1 comparisons if addressof(x) != this; otherwise, no comparisons are performed.
Remarks: Stable ([algorithm.stable]).
If addressof(x) != this, x is empty after the merge.
No elements are copied by this operation.
If an exception is thrown other than by a comparison there are no effects.
constexpr void reverse() noexcept;
Effects: Reverses the order of the elements in the list.
Does not affect the validity of iterators and references.
Complexity: Linear time.
constexpr void sort(); template<class Compare> constexpr void sort(Compare comp);
Effects: Sorts the list according to the operator< or a Compare function object.
If an exception is thrown, the order of the elements in *this is unspecified.
Does not affect the validity of iterators and references.
Complexity: Approximately comparisons, where N == size().
Remarks: Stable.
205)205)
As specified in [allocator.requirements], the requirements in this Clause apply only to lists whose allocators compare equal.

24.3.11.6 Erasure [list.erasure]

template<class T, class Allocator, class U = T> constexpr typename list<T, Allocator>::size_type erase(list<T, Allocator>& c, const U& value);
Effects: Equivalent to: return erase_if(c, [&](auto& elem) { return elem == value; });
template<class T, class Allocator, class Predicate> constexpr typename list<T, Allocator>::size_type erase_if(list<T, Allocator>& c, Predicate pred);
Effects: Equivalent to: return c.remove_if(pred);

24.4 Associative containers [associative]

24.4.1 General [associative.general]

The header <map> defines the class templates map and multimap; the header <set> defines the class templates set and multiset.
The following exposition-only alias templates may appear in deduction guides for associative containers: template<class InputIterator> using iter-value-type = typename iterator_traits<InputIterator>::value_type; // exposition only template<class InputIterator> using iter-key-type = remove_const_t< tuple_element_t<0, iter-value-type<InputIterator>>>; // exposition only template<class InputIterator> using iter-mapped-type = tuple_element_t<1, iter-value-type<InputIterator>>; // exposition only template<class InputIterator> using iter-to-alloc-type = pair< add_const_t<tuple_element_t<0, iter-value-type<InputIterator>>>, tuple_element_t<1, iter-value-type<InputIterator>>>; // exposition only template<ranges::input_range Range> using range-key-type = remove_const_t<typename ranges::range_value_t<Range>::first_type>; // exposition only template<ranges::input_range Range> using range-mapped-type = typename ranges::range_value_t<Range>::second_type; // exposition only template<ranges::input_range Range> using range-to-alloc-type = pair<add_const_t<typename ranges::range_value_t<Range>::first_type>, typename ranges::range_value_t<Range>::second_type>; // exposition only

24.4.2 Header <map> synopsis [associative.map.syn]

#include <compare> // see [compare.syn] #include <initializer_list> // see [initializer.list.syn] namespace std { // [map], class template map template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>> class map; template<class Key, class T, class Compare, class Allocator> constexpr bool operator==(const map<Key, T, Compare, Allocator>& x, const map<Key, T, Compare, Allocator>& y); template<class Key, class T, class Compare, class Allocator> constexpr synth-three-way-result<pair<const Key, T>> operator<=>(const map<Key, T, Compare, Allocator>& x, const map<Key, T, Compare, Allocator>& y); template<class Key, class T, class Compare, class Allocator> constexpr void swap(map<Key, T, Compare, Allocator>& x, map<Key, T, Compare, Allocator>& y) noexcept(noexcept(x.swap(y))); // [map.erasure], erasure for map template<class Key, class T, class Compare, class Allocator, class Predicate> constexpr typename map<Key, T, Compare, Allocator>::size_type erase_if(map<Key, T, Compare, Allocator>& c, Predicate pred); // [multimap], class template multimap template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>> class multimap; template<class Key, class T, class Compare, class Allocator> constexpr bool operator==(const multimap<Key, T, Compare, Allocator>& x, const multimap<Key, T, Compare, Allocator>& y); template<class Key, class T, class Compare, class Allocator> constexpr synth-three-way-result<pair<const Key, T>> operator<=>(const multimap<Key, T, Compare, Allocator>& x, const multimap<Key, T, Compare, Allocator>& y); template<class Key, class T, class Compare, class Allocator> constexpr void swap(multimap<Key, T, Compare, Allocator>& x, multimap<Key, T, Compare, Allocator>& y) noexcept(noexcept(x.swap(y))); // [multimap.erasure], erasure for multimap template<class Key, class T, class Compare, class Allocator, class Predicate> constexpr typename multimap<Key, T, Compare, Allocator>::size_type erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred); namespace pmr { template<class Key, class T, class Compare = less<Key>> using map = std::map<Key, T, Compare, polymorphic_allocator<pair<const Key, T>>>; template<class Key, class T, class Compare = less<Key>> using multimap = std::multimap<Key, T, Compare, polymorphic_allocator<pair<const Key, T>>>; } }

24.4.3 Header <set> synopsis [associative.set.syn]

#include <compare> // see [compare.syn] #include <initializer_list> // see [initializer.list.syn] namespace std { // [set], class template set template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>> class set; template<class Key, class Compare, class Allocator> constexpr bool operator==(const set<Key, Compare, Allocator>& x, const set<Key, Compare, Allocator>& y); template<class Key, class Compare, class Allocator> constexpr synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x, const set<Key, Compare, Allocator>& y); template<class Key, class Compare, class Allocator> constexpr void swap(set<Key, Compare, Allocator>& x, set<Key, Compare, Allocator>& y) noexcept(noexcept(x.swap(y))); // [set.erasure], erasure for set template<class Key, class Compare, class Allocator, class Predicate> constexpr typename set<Key, Compare, Allocator>::size_type erase_if(set<Key, Compare, Allocator>& c, Predicate pred); // [multiset], class template multiset template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>> class multiset; template<class Key, class Compare, class Allocator> constexpr bool operator==(const multiset<Key, Compare, Allocator>& x, const multiset<Key, Compare, Allocator>& y); template<class Key, class Compare, class Allocator> constexpr synth-three-way-result<Key> operator<=>(const multiset<Key, Compare, Allocator>& x, const multiset<Key, Compare, Allocator>& y); template<class Key, class Compare, class Allocator> constexpr void swap(multiset<Key, Compare, Allocator>& x, multiset<Key, Compare, Allocator>& y) noexcept(noexcept(x.swap(y))); // [multiset.erasure], erasure for multiset template<class Key, class Compare, class Allocator, class Predicate> constexpr typename multiset<Key, Compare, Allocator>::size_type erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred); namespace pmr { template<class Key, class Compare = less<Key>> using set = std::set<Key, Compare, polymorphic_allocator<Key>>; template<class Key, class Compare = less<Key>> using multiset = std::multiset<Key, Compare, polymorphic_allocator<Key>>; } }

24.4.4 Class template map [map]

24.4.4.1 Overview [map.overview]

A map is an associative container that supports unique keys (i.e., contains at most one of each key value) and provides for fast retrieval of values of another type T based on the keys.
The map class supports bidirectional iterators.
A map meets all of the requirements of a container ([container.reqmts]), of a reversible container ([container.rev.reqmts]), of an allocator-aware container ([container.alloc.reqmts]).
and of an associative container ([associative.reqmts]).
A map also provides most operations described in [associative.reqmts] for unique keys.
This means that a map supports the a_uniq operations in [associative.reqmts] but not the a_eq operations.
For a map<Key,T> the key_type is Key and the value_type is pair<const Key,T>.
Descriptions are provided here only for operations on map that are not described in one of those tables or for operations where there is additional semantic information.
The types iterator and const_iterator meet the constexpr iterator requirements ([iterator.requirements.general]).
namespace std { template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>> class map { public: // types using key_type = Key; using mapped_type = T; using value_type = pair<const Key, T>; using key_compare = Compare; using allocator_type = Allocator; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; using node_type = unspecified; using insert_return_type = insert-return-type<iterator, node_type>; class value_compare { friend class map; protected: Compare comp; constexpr value_compare(Compare c) : comp(c) {} public: constexpr bool operator()(const value_type& x, const value_type& y) const { return comp(x.first, y.first); } }; // [map.cons], construct/copy/destroy constexpr map() : map(Compare()) { } constexpr explicit map(const Compare& comp, const Allocator& = Allocator()); template<class InputIterator> constexpr map(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator()); template<container-compatible-range<value_type> R> constexpr map(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); constexpr map(const map& x); constexpr map(map&& x); explicit map(const Allocator&); constexpr map(const map&, const type_identity_t<Allocator>&); constexpr map(map&&, const type_identity_t<Allocator>&); constexpr map(initializer_list<value_type>, const Compare& = Compare(), const Allocator& = Allocator()); template<class InputIterator> constexpr map(InputIterator first, InputIterator last, const Allocator& a) : map(first, last, Compare(), a) { } template<container-compatible-range<value_type> R> constexpr map(from_range_t, R&& rg, const Allocator& a)) : map(from_range, std::forward<R>(rg), Compare(), a) { } constexpr map(initializer_list<value_type> il, const Allocator& a) : map(il, Compare(), a) { } constexpr ~map(); constexpr map& operator=(const map& x); constexpr map& operator=(map&& x) noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Compare>); constexpr map& operator=(initializer_list<value_type>); constexpr allocator_type get_allocator() const noexcept; // iterators constexpr iterator begin() noexcept; constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; constexpr const_iterator end() const noexcept; constexpr reverse_iterator rbegin() noexcept; constexpr const_reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() noexcept; constexpr const_reverse_iterator rend() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; // capacity constexpr bool empty() const noexcept; constexpr size_type size() const noexcept; constexpr size_type max_size() const noexcept; // [map.access], element access constexpr mapped_type& operator[](const key_type& x); constexpr mapped_type& operator[](key_type&& x); template<class K> constexpr mapped_type& operator[](K&& x); constexpr mapped_type& at(const key_type& x); constexpr const mapped_type& at(const key_type& x) const; template<class K> constexpr mapped_type& at(const K& x); template<class K> constexpr const mapped_type& at(const K& x) const; // [map.modifiers], modifiers template<class... Args> constexpr pair<iterator, bool> emplace(Args&&... args); template<class... Args> constexpr iterator emplace_hint(const_iterator position, Args&&... args); pair<iterator, bool> constexpr insert(const value_type& x); pair<iterator, bool> constexpr insert(value_type&& x); template<class P> constexpr pair<iterator, bool> insert(P&& x); constexpr iterator insert(const_iterator position, const value_type& x); constexpr iterator insert(const_iterator position, value_type&& x); template<class P> constexpr iterator insert(const_iterator position, P&&); template<class InputIterator> constexpr void insert(InputIterator first, InputIterator last); template<container-compatible-range<value_type> R> constexpr void insert_range(R&& rg); constexpr void insert(initializer_list<value_type>); constexpr node_type extract(const_iterator position); constexpr node_type extract(const key_type& x); template<class K> constexpr node_type extract(K&& x); constexpr insert_return_type insert(node_type&& nh); constexpr iterator insert(const_iterator hint, node_type&& nh); template<class... Args> constexpr pair<iterator, bool> try_emplace(const key_type& k, Args&&... args); template<class... Args> constexpr pair<iterator, bool> try_emplace(key_type&& k, Args&&... args); template<class K, class... Args> constexpr pair<iterator, bool> try_emplace(K&& k, Args&&... args); template<class... Args> constexpr iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); template<class... Args> constexpr iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); template<class K, class... Args> constexpr iterator try_emplace(const_iterator hint, K&& k, Args&&... args); template<class M> constexpr pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj); template<class M> constexpr pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template<class K, class M> constexpr pair<iterator, bool> insert_or_assign(K&& k, M&& obj); template<class M> constexpr iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template<class M> constexpr iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); template<class K, class M> constexpr iterator insert_or_assign(const_iterator hint, K&& k, M&& obj); constexpr iterator erase(iterator position); constexpr iterator erase(const_iterator position); constexpr size_type erase(const key_type& x); template<class K> constexpr size_type erase(K&& x); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void swap(map&) noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Compare>); constexpr void clear() noexcept; template<class C2> constexpr void merge(map<Key, T, C2, Allocator>& source); template<class C2> constexpr void merge(map<Key, T, C2, Allocator>&& source); template<class C2> constexpr void merge(multimap<Key, T, C2, Allocator>& source); template<class C2> constexpr void merge(multimap<Key, T, C2, Allocator>&& source); // observers constexpr key_compare key_comp() const; constexpr value_compare value_comp() const; // map operations constexpr iterator find(const key_type& x); constexpr const_iterator find(const key_type& x) const; template<class K> constexpr iterator find(const K& x); template<class K> constexpr const_iterator find(const K& x) const; constexpr size_type count(const key_type& x) const; template<class K> constexpr size_type count(const K& x) const; constexpr bool contains(const key_type& x) const; template<class K> constexpr bool contains(const K& x) const; constexpr iterator lower_bound(const key_type& x); constexpr const_iterator lower_bound(const key_type& x) const; template<class K> constexpr iterator lower_bound(const K& x); template<class K> constexpr const_iterator lower_bound(const K& x) const; constexpr iterator upper_bound(const key_type& x); constexpr const_iterator upper_bound(const key_type& x) const; template<class K> constexpr iterator upper_bound(const K& x); template<class K> constexpr const_iterator upper_bound(const K& x) const; constexpr pair<iterator, iterator> equal_range(const key_type& x); constexpr pair<const_iterator, const_iterator> equal_range(const key_type& x) const; template<class K> constexpr pair<iterator, iterator> equal_range(const K& x); template<class K> constexpr pair<const_iterator, const_iterator> equal_range(const K& x) const; }; template<class InputIterator, class Compare = less<iter-key-type<InputIterator>>, class Allocator = allocator<iter-to-alloc-type<InputIterator>>> map(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator()) -> map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Compare, Allocator>; template<ranges::input_range R, class Compare = less<range-key-type<R>, class Allocator = allocator<range-to-alloc-type<R>>> map(from_range_t, R&&, Compare = Compare(), Allocator = Allocator()) -> map<range-key-type<R>, range-mapped-type<R>, Compare, Allocator>; template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>> map(initializer_list<pair<Key, T>>, Compare = Compare(), Allocator = Allocator()) -> map<Key, T, Compare, Allocator>; template<class InputIterator, class Allocator> map(InputIterator, InputIterator, Allocator) -> map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, less<iter-key-type<InputIterator>>, Allocator>; template<ranges::input_range R, class Allocator> map(from_range_t, R&&, Allocator) -> map<range-key-type<R>, range-mapped-type<R>, less<range-key-type<R>>, Allocator>; template<class Key, class T, class Allocator> map(initializer_list<pair<Key, T>>, Allocator) -> map<Key, T, less<Key>, Allocator>; }

24.4.4.2 Constructors, copy, and assignment [map.cons]

constexpr explicit map(const Compare& comp, const Allocator& = Allocator());
Effects: Constructs an empty map using the specified comparison object and allocator.
Complexity: Constant.
template<class InputIterator> constexpr map(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator());
Effects: Constructs an empty map using the specified comparison object and allocator, and inserts elements from the range [first, last).
Complexity: Linear in N if the range [first, last) is already sorted with respect to comp and otherwise , where N is last - first.
template<container-compatible-range<value_type> R> constexpr map(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator());
Effects: Constructs an empty map using the specified comparison object and allocator, and inserts elements from the range rg.
Complexity: Linear in N if rg is already sorted with respect to comp and otherwise , where N is ranges​::​distance(rg).

24.4.4.3 Element access [map.access]

constexpr mapped_type& operator[](const key_type& x);
Effects: Equivalent to: return try_emplace(x).first->second;
constexpr mapped_type& operator[](key_type&& x);
Effects: Equivalent to: return try_emplace(std​::​move(x)).first->second;
template<class K> constexpr mapped_type& operator[](K&& x);
Constraints: The qualified-id Compare​::​is_transparent is valid and denotes a type.
Effects: Equivalent to: return try_emplace(std​::​forward<K>(x)).first->second;
constexpr mapped_type& at(const key_type& x); constexpr const mapped_type& at(const key_type& x) const;
Returns: A reference to the mapped_type corresponding to x in *this.
Throws: An exception object of type out_of_range if no such element is present.
Complexity: Logarithmic.
template<class K> constexpr mapped_type& at(const K& x); template<class K> constexpr const mapped_type& at(const K& x) const;
Constraints: The qualified-id Compare​::​is_transparent is valid and denotes a type.
Preconditions: The expression find(x) is well-formed and has well-defined behavior.
Returns: A reference to find(x)->second.
Throws: An exception object of type out_of_range if find(x) == end() is true.
Complexity: Logarithmic.

24.4.4.4 Modifiers [map.modifiers]

template<class P> constexpr pair<iterator, bool> insert(P&& x); template<class P> constexpr iterator insert(const_iterator position, P&& x);
Constraints: is_constructible_v<value_type, P&&> is true.
Effects: The first form is equivalent to return emplace(std​::​forward<P>(x)).
The second form is equivalent to return emplace_hint(position, std​::​forward<P>(x)).
template<class... Args> constexpr pair<iterator, bool> try_emplace(const key_type& k, Args&&... args); template<class... Args> constexpr iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);
Preconditions: value_type is Cpp17EmplaceConstructible into map from piecewise_construct, forward_as_tuple(k), forward_as_tuple(std​::​forward<Args>(args)...).
Effects: If the map already contains an element whose key is equivalent to k, there is no effect.
Otherwise inserts an object of type value_type constructed with piecewise_construct, forward_as_tuple(k), forward_as_tuple(std​::​forward<Args>(args)...).
Returns: In the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.
template<class... Args> constexpr pair<iterator, bool> try_emplace(key_type&& k, Args&&... args); template<class... Args> constexpr iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);
Preconditions: value_type is Cpp17EmplaceConstructible into map from piecewise_construct, forward_as_tuple(std​::​move(k)), forward_as_tuple(std​::​forward<Args>(args)...).
Effects: If the map already contains an element whose key is equivalent to k, there is no effect.
Otherwise inserts an object of type value_type constructed with piecewise_construct, forward_as_tuple(std​::​move(k)), forward_as_tuple(std​::​forward<Args>(args)...).
Returns: In the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.
template<class K, class... Args> constexpr pair<iterator, bool> try_emplace(K&& k, Args&&... args); template<class K, class... Args> constexpr iterator try_emplace(const_iterator hint, K&& k, Args&&... args);
Constraints: The qualified-id Compare​::​is_transparent is valid and denotes a type.
For the first overload, is_convertible_v<K&&, const_iterator> and is_convertible_v<K&&, iterator> are both false.
Preconditions: value_type is Cpp17EmplaceConstructible into map from piecewise_construct, forward_as_tuple(std​::​forward<K>(k)), forward_as_tuple(std​::​forward<Args>(args)...).
Effects: If the map already contains an element whose key is equivalent to k, there is no effect.
Otherwise, let r be equal_range(k).
Constructs an object u of type value_type with piecewise_construct, forward_as_tuple(std​::​forward<K>(k)), forward_as_tuple(std​::​forward<Args>(args)...).
If equal_range(u.first) == r is false, the behavior is undefined.
Inserts u into *this.
Returns: For the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.
template<class M> constexpr pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj); template<class M> constexpr iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);
Mandates: is_assignable_v<mapped_type&, M&&> is true.
Preconditions: value_type is Cpp17EmplaceConstructible into map from k, std​::​forward<M>(obj).
Effects: If the map already contains an element e whose key is equivalent to k, assigns std​::​forward<M>(obj) to e.second.
Otherwise inserts an object of type value_type constructed with k, std​::​forward<M>(obj).
Returns: In the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.
template<class M> constexpr pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template<class M> constexpr iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);
Mandates: is_assignable_v<mapped_type&, M&&> is true.
Preconditions: value_type is Cpp17EmplaceConstructible into map from std​::​move(k), std​::​forward<M>(obj).
Effects: If the map already contains an element e whose key is equivalent to k, assigns std​::​forward<M>(obj) to e.second.
Otherwise inserts an object of type value_type constructed with std​::​​move(k), std​::​forward<M>(obj).
Returns: In the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.
template<class K, class M> constexpr pair<iterator, bool> insert_or_assign(K&& k, M&& obj); template<class K, class M> constexpr iterator insert_or_assign(const_iterator hint, K&& k, M&& obj);
Constraints: The qualified-id Compare​::​is_transparent is valid and denotes a type.
Mandates: is_assignable_v<mapped_type&, M&&> is true.
Preconditions: value_type is Cpp17EmplaceConstructible into map from std​::​forward<K>(k), std​::​
forward<M>(obj)
.
Effects: If the map already contains an element e whose key is equivalent to k, assigns std​::​forward<M>
(obj)
to e.second.
Otherwise, let r be equal_range(k).
Constructs an object u of type value_type with std​::​forward<K>(k), std​::​forward<M>(obj).
If equal_range(u.first) == r is false, the behavior is undefined.
Inserts u into *this.
Returns: For the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.

24.4.4.5 Erasure [map.erasure]

template<class Key, class T, class Compare, class Allocator, class Predicate> constexpr typename map<Key, T, Compare, Allocator>::size_type erase_if(map<Key, T, Compare, Allocator>& c, Predicate pred);
Effects: Equivalent to: auto original_size = c.size(); for (auto i = c.begin(), last = c.end(); i != last; ) { if (pred(*i)) { i = c.erase(i); } else { ++i; } } return original_size - c.size();

24.4.5 Class template multimap [multimap]

24.4.5.1 Overview [multimap.overview]

A multimap is an associative container that supports equivalent keys (i.e., possibly containing multiple copies of the same key value) and provides for fast retrieval of values of another type T based on the keys.
The multimap class supports bidirectional iterators.
A multimap meets all of the requirements of a container ([container.reqmts]), of a reversible container ([container.rev.reqmts]), of an allocator-aware container ([container.alloc.reqmts]), and of an associative container ([associative.reqmts]).
A multimap also provides most operations described in [associative.reqmts] for equal keys.
This means that a multimap supports the a_eq operations in [associative.reqmts] but not the a_uniq operations.
For a multimap<Key,T> the key_type is Key and the value_type is pair<const Key,T>.
Descriptions are provided here only for operations on multimap that are not described in one of those tables or for operations where there is additional semantic information.
The types iterator and const_iterator meet the constexpr iterator requirements ([iterator.requirements.general]).
namespace std { template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>> class multimap { public: // types using key_type = Key; using mapped_type = T; using value_type = pair<const Key, T>; using key_compare = Compare; using allocator_type = Allocator; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; using node_type = unspecified; class value_compare { friend class multimap; protected: Compare comp; constexpr value_compare(Compare c) : comp(c) { } public: constexpr bool operator()(const value_type& x, const value_type& y) const { return comp(x.first, y.first); } }; // [multimap.cons], construct/copy/destroy constexpr multimap() : multimap(Compare()) { } constexpr explicit multimap(const Compare& comp, const Allocator& = Allocator()); template<class InputIterator> constexpr multimap(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator()); template<container-compatible-range<value_type> R> constexpr multimap(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); constexpr multimap(const multimap& x); constexpr multimap(multimap&& x); constexpr explicit multimap(const Allocator&); constexpr multimap(const multimap&, const type_identity_t<Allocator>&); constexpr multimap(multimap&&, const type_identity_t<Allocator>&); constexpr multimap(initializer_list<value_type>, const Compare& = Compare(), const Allocator& = Allocator()); template<class InputIterator> constexpr multimap(InputIterator first, InputIterator last, const Allocator& a) : multimap(first, last, Compare(), a) { } template<container-compatible-range<value_type> R> constexpr multimap(from_range_t, R&& rg, const Allocator& a)) : multimap(from_range, std::forward<R>(rg), Compare(), a) { } constexpr multimap(initializer_list<value_type> il, const Allocator& a) : multimap(il, Compare(), a) { } constexpr ~multimap(); constexpr multimap& operator=(const multimap& x); constexpr multimap& operator=(multimap&& x) noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Compare>); constexpr multimap& operator=(initializer_list<value_type>); constexpr allocator_type get_allocator() const noexcept; // iterators constexpr iterator begin() noexcept; constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; constexpr const_iterator end() const noexcept; constexpr reverse_iterator rbegin() noexcept; constexpr const_reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() noexcept; constexpr const_reverse_iterator rend() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; // capacity constexpr bool empty() const noexcept; constexpr size_type size() const noexcept; constexpr size_type max_size() const noexcept; // [multimap.modifiers], modifiers template<class... Args> constexpr iterator emplace(Args&&... args); template<class... Args> constexpr iterator emplace_hint(const_iterator position, Args&&... args); constexpr iterator insert(const value_type& x); constexpr iterator insert(value_type&& x); template<class P> constexpr iterator insert(P&& x); constexpr iterator insert(const_iterator position, const value_type& x); constexpr iterator insert(const_iterator position, value_type&& x); template<class P> constexpr iterator insert(const_iterator position, P&& x); template<class InputIterator> constexpr void insert(InputIterator first, InputIterator last); template<container-compatible-range<value_type> R> constexpr void insert_range(R&& rg); constexpr void insert(initializer_list<value_type>); constexpr node_type extract(const_iterator position); constexpr node_type extract(const key_type& x); template<class K> constexpr node_type extract(K&& x); constexpr iterator insert(node_type&& nh); constexpr iterator insert(const_iterator hint, node_type&& nh); constexpr iterator erase(iterator position); constexpr iterator erase(const_iterator position); constexpr size_type erase(const key_type& x); template<class K> constexpr size_type erase(K&& x); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void swap(multimap&) noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Compare>); constexpr void clear() noexcept; template<class C2> constexpr void merge(multimap<Key, T, C2, Allocator>& source); template<class C2> constexpr void merge(multimap<Key, T, C2, Allocator>&& source); template<class C2> constexpr void merge(map<Key, T, C2, Allocator>& source); template<class C2> constexpr void merge(map<Key, T, C2, Allocator>&& source); // observers constexpr key_compare key_comp() const; constexpr value_compare value_comp() const; // map operations constexpr iterator find(const key_type& x); constexpr const_iterator find(const key_type& x) const; template<class K> constexpr iterator find(const K& x); template<class K> constexpr const_iterator find(const K& x) const; constexpr size_type count(const key_type& x) const; template<class K> constexpr size_type count(const K& x) const; constexpr bool contains(const key_type& x) const; template<class K> constexpr bool contains(const K& x) const; constexpr iterator lower_bound(const key_type& x); constexpr const_iterator lower_bound(const key_type& x) const; template<class K> constexpr iterator lower_bound(const K& x); template<class K> constexpr const_iterator lower_bound(const K& x) const; constexpr iterator upper_bound(const key_type& x); constexpr const_iterator upper_bound(const key_type& x) const; template<class K> constexpr iterator upper_bound(const K& x); template<class K> constexpr const_iterator upper_bound(const K& x) const; constexpr pair<iterator, iterator> equal_range(const key_type& x); constexpr pair<const_iterator, const_iterator> equal_range(const key_type& x) const; template<class K> constexpr pair<iterator, iterator> equal_range(const K& x); template<class K> constexpr pair<const_iterator, const_iterator> equal_range(const K& x) const; }; template<class InputIterator, class Compare = less<iter-key-type<InputIterator>>, class Allocator = allocator<iter-to-alloc-type<InputIterator>>> multimap(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator()) -> multimap<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Compare, Allocator>; template<ranges::input_range R, class Compare = less<range-key-type<R>>, class Allocator = allocator<range-to-alloc-type<R>>> multimap(from_range_t, R&&, Compare = Compare(), Allocator = Allocator()) -> multimap<range-key-type<R>, range-mapped-type<R>, Compare, Allocator>; template<class Key, class T, class Compare = less<Key>, class Allocator = allocator<pair<const Key, T>>> multimap(initializer_list<pair<Key, T>>, Compare = Compare(), Allocator = Allocator()) -> multimap<Key, T, Compare, Allocator>; template<class InputIterator, class Allocator> multimap(InputIterator, InputIterator, Allocator) -> multimap<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, less<iter-key-type<InputIterator>>, Allocator>; template<ranges::input_range R, class Allocator> multimap(from_range_t, R&&, Allocator) -> multimap<range-key-type<R>, range-mapped-type<R>, less<range-key-type<R>>, Allocator>; template<class Key, class T, class Allocator> multimap(initializer_list<pair<Key, T>>, Allocator) -> multimap<Key, T, less<Key>, Allocator>; }

24.4.5.2 Constructors [multimap.cons]

constexpr explicit multimap(const Compare& comp, const Allocator& = Allocator());
Effects: Constructs an empty multimap using the specified comparison object and allocator.
Complexity: Constant.
template<class InputIterator> constexpr multimap(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator());
Effects: Constructs an empty multimap using the specified comparison object and allocator, and inserts elements from the range [first, last).
Complexity: Linear in N if the range [first, last) is already sorted with respect to comp and otherwise , where N is last - first.
template<container-compatible-range<value_type> R> constexpr multimap(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator());
Effects: Constructs an empty multimap using the specified comparison object and allocator, and inserts elements from the range rg.
Complexity: Linear in N if rg is already sorted with respect to comp and otherwise , where N is ranges​::​distance(rg).

24.4.5.3 Modifiers [multimap.modifiers]

template<class P> constexpr iterator insert(P&& x); template<class P> constexpr iterator insert(const_iterator position, P&& x);
Constraints: is_constructible_v<value_type, P&&> is true.
Effects: The first form is equivalent to return emplace(std​::​forward<P>(x)).
The second form is equivalent to return emplace_hint(position, std​::​forward<P>(x)).

24.4.5.4 Erasure [multimap.erasure]

template<class Key, class T, class Compare, class Allocator, class Predicate> constexpr typename multimap<Key, T, Compare, Allocator>::size_type erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred);
Effects: Equivalent to: auto original_size = c.size(); for (auto i = c.begin(), last = c.end(); i != last; ) { if (pred(*i)) { i = c.erase(i); } else { ++i; } } return original_size - c.size();

24.4.6 Class template set [set]

24.4.6.1 Overview [set.overview]

A set is an associative container that supports unique keys (i.e., contains at most one of each key value) and provides for fast retrieval of the keys themselves.
The set class supports bidirectional iterators.
A set meets all of the requirements of a container ([container.reqmts]), of a reversible container ([container.rev.reqmts]), of an allocator-aware container ([container.alloc.reqmts]).
and of an associative container ([associative.reqmts]).
A set also provides most operations described in [associative.reqmts] for unique keys.
This means that a set supports the a_uniq operations in [associative.reqmts] but not the a_eq operations.
For a set<Key> both the key_type and value_type are Key.
Descriptions are provided here only for operations on set that are not described in one of these tables and for operations where there is additional semantic information.
The types iterator and const_iterator meet the constexpr iterator requirements ([iterator.requirements.general]).
namespace std { template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>> class set { public: // types using key_type = Key; using key_compare = Compare; using value_type = Key; using value_compare = Compare; using allocator_type = Allocator; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; using node_type = unspecified; using insert_return_type = insert-return-type<iterator, node_type>; // [set.cons], construct/copy/destroy constexpr set() : set(Compare()) { } constexpr explicit set(const Compare& comp, const Allocator& = Allocator()); template<class InputIterator> constexpr set(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator()); template<container-compatible-range<value_type> R> constexpr set(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); constexpr set(const set& x); constexpr set(set&& x); constexpr explicit set(const Allocator&); constexpr set(const set&, const type_identity_t<Allocator>&); constexpr set(set&&, const type_identity_t<Allocator>&); constexpr set(initializer_list<value_type>, const Compare& = Compare(), const Allocator& = Allocator()); template<class InputIterator> constexpr set(InputIterator first, InputIterator last, const Allocator& a) : set(first, last, Compare(), a) { } template<container-compatible-range<value_type> R> constexpr set(from_range_t, R&& rg, const Allocator& a)) : set(from_range, std::forward<R>(rg), Compare(), a) { } constexpr set(initializer_list<value_type> il, const Allocator& a) : set(il, Compare(), a) { } constexpr ~set(); constexpr set& operator=(const set& x); constexpr set& operator=(set&& x) noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Compare>); constexpr set& operator=(initializer_list<value_type>); constexpr allocator_type get_allocator() const noexcept; // iterators constexpr iterator begin() noexcept; constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; constexpr const_iterator end() const noexcept; constexpr reverse_iterator rbegin() noexcept; constexpr const_reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() noexcept; constexpr const_reverse_iterator rend() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; // capacity constexpr bool empty() const noexcept; constexpr size_type size() const noexcept; constexpr size_type max_size() const noexcept; // [set.modifiers], modifiers template<class... Args> constexpr pair<iterator, bool> emplace(Args&&... args); template<class... Args> constexpr iterator emplace_hint(const_iterator position, Args&&... args); constexpr pair<iterator,bool> insert(const value_type& x); constexpr pair<iterator,bool> insert(value_type&& x); template<class K> constexpr pair<iterator, bool> insert(K&& x); constexpr iterator insert(const_iterator position, const value_type& x); constexpr iterator insert(const_iterator position, value_type&& x); template<class K> constexpr iterator insert(const_iterator position, K&& x); template<class InputIterator> constexpr void insert(InputIterator first, InputIterator last); template<container-compatible-range<value_type> R> constexpr void insert_range(R&& rg); constexpr void insert(initializer_list<value_type>); constexpr node_type extract(const_iterator position); constexpr node_type extract(const key_type& x); template<class K> constexpr node_type extract(K&& x); constexpr insert_return_type insert(node_type&& nh); constexpr iterator insert(const_iterator hint, node_type&& nh); constexpr iterator erase(iterator position) requires (!same_as<iterator, const_iterator>); constexpr iterator erase(const_iterator position); constexpr size_type erase(const key_type& x); template<class K> constexpr size_type erase(K&& x); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void swap(set&) noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Compare>); constexpr void clear() noexcept; template<class C2> constexpr void merge(set<Key, C2, Allocator>& source); template<class C2> constexpr void merge(set<Key, C2, Allocator>&& source); template<class C2> constexpr void merge(multiset<Key, C2, Allocator>& source); template<class C2> constexpr void merge(multiset<Key, C2, Allocator>&& source); // observers constexpr key_compare key_comp() const; constexpr value_compare value_comp() const; // set operations constexpr iterator find(const key_type& x); constexpr const_iterator find(const key_type& x) const; template<class K> constexpr iterator find(const K& x); template<class K> constexpr const_iterator find(const K& x) const; constexpr size_type count(const key_type& x) const; template<class K> constexpr size_type count(const K& x) const; constexpr bool contains(const key_type& x) const; template<class K> constexpr bool contains(const K& x) const; constexpr iterator lower_bound(const key_type& x); constexpr const_iterator lower_bound(const key_type& x) const; template<class K> constexpr iterator lower_bound(const K& x); template<class K> constexpr const_iterator lower_bound(const K& x) const; constexpr iterator upper_bound(const key_type& x); constexpr const_iterator upper_bound(const key_type& x) const; template<class K> constexpr iterator upper_bound(const K& x); template<class K> constexpr const_iterator upper_bound(const K& x) const; constexpr pair<iterator, iterator> equal_range(const key_type& x); constexpr pair<const_iterator, const_iterator> equal_range(const key_type& x) const; template<class K> constexpr pair<iterator, iterator> equal_range(const K& x); template<class K> constexpr pair<const_iterator, const_iterator> equal_range(const K& x) const; }; template<class InputIterator, class Compare = less<iter-value-type<InputIterator>>, class Allocator = allocator<iter-value-type<InputIterator>>> set(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator()) -> set<iter-value-type<InputIterator>, Compare, Allocator>; template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>, class Allocator = allocator<ranges::range_value_t<R>>> set(from_range_t, R&&, Compare = Compare(), Allocator = Allocator()) -> set<ranges::range_value_t<R>, Compare, Allocator>; template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>> set(initializer_list<Key>, Compare = Compare(), Allocator = Allocator()) -> set<Key, Compare, Allocator>; template<class InputIterator, class Allocator> set(InputIterator, InputIterator, Allocator) -> set<iter-value-type<InputIterator>, less<iter-value-type<InputIterator>>, Allocator>; template<ranges::input_range R, class Allocator> set(from_range_t, R&&, Allocator) -> set<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>; template<class Key, class Allocator> set(initializer_list<Key>, Allocator) -> set<Key, less<Key>, Allocator>; }

24.4.6.2 Constructors, copy, and assignment [set.cons]

constexpr explicit set(const Compare& comp, const Allocator& = Allocator());
Effects: Constructs an empty set using the specified comparison object and allocator.
Complexity: Constant.
template<class InputIterator> constexpr set(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator());
Effects: Constructs an empty set using the specified comparison object and allocator, and inserts elements from the range [first, last).
Complexity: Linear in N if the range [first, last) is already sorted with respect to comp and otherwise , where N is last - first.
template<container-compatible-range<value_type> R> constexpr set(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator());
Effects: Constructs an empty set using the specified comparison object and allocator, and inserts elements from the range rg.
Complexity: Linear in N if rg is already sorted with respect to comp and otherwise , where N is ranges​::​distance(rg).

24.4.6.3 Erasure [set.erasure]

template<class Key, class Compare, class Allocator, class Predicate> constexpr typename set<Key, Compare, Allocator>::size_type erase_if(set<Key, Compare, Allocator>& c, Predicate pred);
Effects: Equivalent to: auto original_size = c.size(); for (auto i = c.begin(), last = c.end(); i != last; ) { if (pred(*i)) { i = c.erase(i); } else { ++i; } } return original_size - c.size();

24.4.6.4 Modifiers [set.modifiers]

template<class K> constexpr pair<iterator, bool> insert(K&& x); template<class K> constexpr iterator insert(const_iterator hint, K&& x);
Constraints: The qualified-id Compare​::​is_transparent is valid and denotes a type.
For the second overload, is_convertible_v<K&&, const_iterator> and is_convertible_v<K&&, iterator> are both false.
Preconditions: value_type is Cpp17EmplaceConstructible into set from std​::​forward<K>(x).
Effects: If the set already contains an element that is equivalent to x, there is no effect.
Otherwise, let r be equal_range(x).
Constructs an object u of type value_type with std​::​forward<K>(x).
If equal_range(u) == r is false, the behavior is undefined.
Inserts u into *this.
Returns: For the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the set element that is equivalent to x.
Complexity: Logarithmic.

24.4.7 Class template multiset [multiset]

24.4.7.1 Overview [multiset.overview]

A multiset is an associative container that supports equivalent keys (i.e., possibly contains multiple copies of the same key value) and provides for fast retrieval of the keys themselves.
The multiset class supports bidirectional iterators.
A multiset meets all of the requirements of a container ([container.reqmts]), of a reversible container ([container.rev.reqmts]), of an allocator-aware container ([container.alloc.reqmts]), of an associative container ([associative.reqmts]).
multiset also provides most operations described in [associative.reqmts] for duplicate keys.
This means that a multiset supports the a_eq operations in [associative.reqmts] but not the a_uniq operations.
For a multiset<Key> both the key_type and value_type are Key.
Descriptions are provided here only for operations on multiset that are not described in one of these tables and for operations where there is additional semantic information.
The types iterator and const_iterator meet the constexpr iterator requirements ([iterator.requirements.general]).
namespace std { template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>> class multiset { public: // types using key_type = Key; using key_compare = Compare; using value_type = Key; using value_compare = Compare; using allocator_type = Allocator; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using reverse_iterator = std::reverse_iterator<iterator>; using const_reverse_iterator = std::reverse_iterator<const_iterator>; using node_type = unspecified; // [multiset.cons], construct/copy/destroy constexpr multiset() : multiset(Compare()) { } constexpr explicit multiset(const Compare& comp, const Allocator& = Allocator()); template<class InputIterator> constexpr multiset(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator()); template<container-compatible-range<value_type> R> constexpr multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator()); constexpr multiset(const multiset& x); constexpr multiset(multiset&& x); constexpr explicit multiset(const Allocator&); constexpr multiset(const multiset&, const type_identity_t<Allocator>&); constexpr multiset(multiset&&, const type_identity_t<Allocator>&); constexpr multiset(initializer_list<value_type>, const Compare& = Compare(), const Allocator& = Allocator()); template<class InputIterator> constexpr multiset(InputIterator first, InputIterator last, const Allocator& a) : multiset(first, last, Compare(), a) { } template<container-compatible-range<value_type> R> constexpr multiset(from_range_t, R&& rg, const Allocator& a)) : multiset(from_range, std::forward<R>(rg), Compare(), a) { } constexpr multiset(initializer_list<value_type> il, const Allocator& a) : multiset(il, Compare(), a) { } constexpr ~multiset(); constexpr multiset& operator=(const multiset& x); constexpr multiset& operator=(multiset&& x) noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Compare>); constexpr multiset& operator=(initializer_list<value_type>); constexpr allocator_type get_allocator() const noexcept; // iterators constexpr iterator begin() noexcept; constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; constexpr const_iterator end() const noexcept; constexpr reverse_iterator rbegin() noexcept; constexpr const_reverse_iterator rbegin() const noexcept; constexpr reverse_iterator rend() noexcept; constexpr const_reverse_iterator rend() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; constexpr const_reverse_iterator crbegin() const noexcept; constexpr const_reverse_iterator crend() const noexcept; // capacity constexpr bool empty() const noexcept; constexpr size_type size() const noexcept; constexpr size_type max_size() const noexcept; // modifiers template<class... Args> constexpr iterator emplace(Args&&... args); template<class... Args> constexpr iterator emplace_hint(const_iterator position, Args&&... args); constexpr iterator insert(const value_type& x); constexpr iterator insert(value_type&& x); constexpr iterator insert(const_iterator position, const value_type& x); constexpr iterator insert(const_iterator position, value_type&& x); template<class InputIterator> constexpr void insert(InputIterator first, InputIterator last); template<container-compatible-range<value_type> R> constexpr void insert_range(R&& rg); constexpr void insert(initializer_list<value_type>); constexpr node_type extract(const_iterator position); constexpr node_type extract(const key_type& x); template<class K> constexpr node_type extract(K&& x); constexpr iterator insert(node_type&& nh); constexpr iterator insert(const_iterator hint, node_type&& nh); constexpr iterator erase(iterator position) requires (!same_as<iterator, const_iterator>); constexpr iterator erase(const_iterator position); constexpr size_type erase(const key_type& x); template<class K> constexpr size_type erase(K&& x); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void swap(multiset&) noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Compare>); constexpr void clear() noexcept; template<class C2> constexpr void merge(multiset<Key, C2, Allocator>& source); template<class C2> constexpr void merge(multiset<Key, C2, Allocator>&& source); template<class C2> constexpr void merge(set<Key, C2, Allocator>& source); template<class C2> constexpr void merge(set<Key, C2, Allocator>&& source); // observers constexpr key_compare key_comp() const; constexpr value_compare value_comp() const; // set operations constexpr iterator find(const key_type& x); constexpr const_iterator find(const key_type& x) const; template<class K> constexpr iterator find(const K& x); template<class K> constexpr const_iterator find(const K& x) const; constexpr size_type count(const key_type& x) const; template<class K> constexpr size_type count(const K& x) const; constexpr bool contains(const key_type& x) const; template<class K> constexpr bool contains(const K& x) const; constexpr iterator lower_bound(const key_type& x); constexpr const_iterator lower_bound(const key_type& x) const; template<class K> constexpr iterator lower_bound(const K& x); template<class K> constexpr const_iterator lower_bound(const K& x) const; constexpr iterator upper_bound(const key_type& x); constexpr const_iterator upper_bound(const key_type& x) const; template<class K> constexpr iterator upper_bound(const K& x); template<class K> constexpr const_iterator upper_bound(const K& x) const; constexpr pair<iterator, iterator> equal_range(const key_type& x); constexpr pair<const_iterator, const_iterator> equal_range(const key_type& x) const; template<class K> constexpr pair<iterator, iterator> equal_range(const K& x); template<class K> constexpr pair<const_iterator, const_iterator> equal_range(const K& x) const; }; template<class InputIterator, class Compare = less<iter-value-type<InputIterator>>, class Allocator = allocator<iter-value-type<InputIterator>>> multiset(InputIterator, InputIterator, Compare = Compare(), Allocator = Allocator()) -> multiset<iter-value-type<InputIterator>, Compare, Allocator>; template<ranges::input_range R, class Compare = less<ranges::range_value_t<R>>, class Allocator = allocator<ranges::range_value_t<R>>> multiset(from_range_t, R&&, Compare = Compare(), Allocator = Allocator()) -> multiset<ranges::range_value_t<R>, Compare, Allocator>; template<class Key, class Compare = less<Key>, class Allocator = allocator<Key>> multiset(initializer_list<Key>, Compare = Compare(), Allocator = Allocator()) -> multiset<Key, Compare, Allocator>; template<class InputIterator, class Allocator> multiset(InputIterator, InputIterator, Allocator) -> multiset<iter-value-type<InputIterator>, less<iter-value-type<InputIterator>>, Allocator>; template<ranges::input_range R, class Allocator> multiset(from_range_t, R&&, Allocator) -> multiset<ranges::range_value_t<R>, less<ranges::range_value_t<R>>, Allocator>; template<class Key, class Allocator> multiset(initializer_list<Key>, Allocator) -> multiset<Key, less<Key>, Allocator>; }

24.4.7.2 Constructors [multiset.cons]

constexpr explicit multiset(const Compare& comp, const Allocator& = Allocator());
Effects: Constructs an empty multiset using the specified comparison object and allocator.
Complexity: Constant.
template<class InputIterator> constexpr multiset(InputIterator first, InputIterator last, const Compare& comp = Compare(), const Allocator& = Allocator());
Effects: Constructs an empty multiset using the specified comparison object and allocator, and inserts elements from the range [first, last).
Complexity: Linear in N if the range [first, last) is already sorted with respect to comp and otherwise , where N is last - first.
template<container-compatible-range<value_type> R> constexpr multiset(from_range_t, R&& rg, const Compare& comp = Compare(), const Allocator& = Allocator());
Effects: Constructs an empty multiset using the specified comparison object and allocator, and inserts elements from the range rg.
Complexity: Linear in N if rg is already sorted with respect to comp and otherwise , where N is ranges​::​distance(rg).

24.4.7.3 Erasure [multiset.erasure]

template<class Key, class Compare, class Allocator, class Predicate> constexpr typename multiset<Key, Compare, Allocator>::size_type erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred);
Effects: Equivalent to: auto original_size = c.size(); for (auto i = c.begin(), last = c.end(); i != last; ) { if (pred(*i)) { i = c.erase(i); } else { ++i; } } return original_size - c.size();

24.5 Unordered associative containers [unord]

24.5.1 General [unord.general]

The header <unordered_map> defines the class templates unordered_map and unordered_multimap; the header <unordered_set> defines the class templates unordered_set and unordered_multiset.
The exposition-only alias templates iter-value-type, iter-key-type, iter-mapped-type, iter-to-alloc-type, range-key-type, range-mapped-type, and range-to-alloc-type defined in [associative.general] may appear in deduction guides for unordered containers.

24.5.2 Header <unordered_map> synopsis [unord.map.syn]

#include <compare> // see [compare.syn] #include <initializer_list> // see [initializer.list.syn] namespace std { // [unord.map], class template unordered_map template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>, class Alloc = allocator<pair<const Key, T>>> class unordered_map; // [unord.multimap], class template unordered_multimap template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>, class Alloc = allocator<pair<const Key, T>>> class unordered_multimap; template<class Key, class T, class Hash, class Pred, class Alloc> constexpr bool operator==(const unordered_map<Key, T, Hash, Pred, Alloc>& a, const unordered_map<Key, T, Hash, Pred, Alloc>& b); template<class Key, class T, class Hash, class Pred, class Alloc> constexpr bool operator==(const unordered_multimap<Key, T, Hash, Pred, Alloc>& a, const unordered_multimap<Key, T, Hash, Pred, Alloc>& b); template<class Key, class T, class Hash, class Pred, class Alloc> constexpr void swap(unordered_map<Key, T, Hash, Pred, Alloc>& x, unordered_map<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); template<class Key, class T, class Hash, class Pred, class Alloc> constexpr void swap(unordered_multimap<Key, T, Hash, Pred, Alloc>& x, unordered_multimap<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // [unord.map.erasure], erasure for unordered_map template<class K, class T, class H, class P, class A, class Predicate> constexpr typename unordered_map<K, T, H, P, A>::size_type erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred); // [unord.multimap.erasure], erasure for unordered_multimap template<class K, class T, class H, class P, class A, class Predicate> constexpr typename unordered_multimap<K, T, H, P, A>::size_type erase_if(unordered_multimap<K, T, H, P, A>& c, Predicate pred); namespace pmr { template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>> using unordered_map = std::unordered_map<Key, T, Hash, Pred, polymorphic_allocator<pair<const Key, T>>>; template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>> using unordered_multimap = std::unordered_multimap<Key, T, Hash, Pred, polymorphic_allocator<pair<const Key, T>>>; } }

24.5.3 Header <unordered_set> synopsis [unord.set.syn]

#include <compare> // see [compare.syn] #include <initializer_list> // see [initializer.list.syn] namespace std { // [unord.set], class template unordered_set template<class Key, class Hash = hash<Key>, class Pred = equal_to<Key>, class Alloc = allocator<Key>> class unordered_set; // [unord.multiset], class template unordered_multiset template<class Key, class Hash = hash<Key>, class Pred = equal_to<Key>, class Alloc = allocator<Key>> class unordered_multiset; template<class Key, class Hash, class Pred, class Alloc> constexpr bool operator==(const unordered_set<Key, Hash, Pred, Alloc>& a, const unordered_set<Key, Hash, Pred, Alloc>& b); template<class Key, class Hash, class Pred, class Alloc> constexpr bool operator==(const unordered_multiset<Key, Hash, Pred, Alloc>& a, const unordered_multiset<Key, Hash, Pred, Alloc>& b); template<class Key, class Hash, class Pred, class Alloc> constexpr void swap(unordered_set<Key, Hash, Pred, Alloc>& x, unordered_set<Key, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); template<class Key, class Hash, class Pred, class Alloc> constexpr void swap(unordered_multiset<Key, Hash, Pred, Alloc>& x, unordered_multiset<Key, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y))); // [unord.set.erasure], erasure for unordered_set template<class K, class H, class P, class A, class Predicate> constexpr typename unordered_set<K, H, P, A>::size_type erase_if(unordered_set<K, H, P, A>& c, Predicate pred); // [unord.multiset.erasure], erasure for unordered_multiset template<class K, class H, class P, class A, class Predicate> constexpr typename unordered_multiset<K, H, P, A>::size_type erase_if(unordered_multiset<K, H, P, A>& c, Predicate pred); namespace pmr { template<class Key, class Hash = hash<Key>, class Pred = equal_to<Key>> using unordered_set = std::unordered_set<Key, Hash, Pred, polymorphic_allocator<Key>>; template<class Key, class Hash = hash<Key>, class Pred = equal_to<Key>> using unordered_multiset = std::unordered_multiset<Key, Hash, Pred, polymorphic_allocator<Key>>; } }

24.5.4 Class template unordered_map [unord.map]

24.5.4.1 Overview [unord.map.overview]

An unordered_map is an unordered associative container that supports unique keys (an unordered_map contains at most one of each key value) and that associates values of another type mapped_type with the keys.
The unordered_map class supports forward iterators.
An unordered_map meets all of the requirements of a container ([container.reqmts]), of an allocator-aware container ([container.alloc.reqmts]), and of an unordered associative container ([unord.req]).
It provides the operations described in the preceding requirements table for unique keys; that is, an unordered_map supports the a_uniq operations in that table, not the a_eq operations.
For an unordered_map<Key, T> the key_type is Key, the mapped_type is T, and the value_type is pair<const Key, T>.
Subclause [unord.map] only describes operations on unordered_map that are not described in one of the requirement tables, or for which there is additional semantic information.
The types iterator and const_iterator meet the constexpr iterator requirements ([iterator.requirements.general]).
namespace std { template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>> class unordered_map { public: // types using key_type = Key; using mapped_type = T; using value_type = pair<const Key, T>; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using local_iterator = implementation-defined; // see [container.requirements] using const_local_iterator = implementation-defined; // see [container.requirements] using node_type = unspecified; using insert_return_type = insert-return-type<iterator, node_type>; // [unord.map.cnstr], construct/copy/destroy constexpr unordered_map(); constexpr explicit unordered_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<class InputIterator> constexpr unordered_map(InputIterator f, InputIterator l, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<container-compatible-range<value_type> R> constexpr unordered_map(from_range_t, R&& rg, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); constexpr unordered_map(const unordered_map&); constexpr unordered_map(unordered_map&&); constexpr explicit unordered_map(const Allocator&); constexpr unordered_map(const unordered_map&, const type_identity_t<Allocator>&); constexpr unordered_map(unordered_map&&, const type_identity_t<Allocator>&); constexpr unordered_map(initializer_list<value_type> il, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); constexpr unordered_map(size_type n, const allocator_type& a) : unordered_map(n, hasher(), key_equal(), a) { } constexpr unordered_map(size_type n, const hasher& hf, const allocator_type& a) : unordered_map(n, hf, key_equal(), a) { } template<class InputIterator> constexpr unordered_map(InputIterator f, InputIterator l, size_type n, const allocator_type& a) : unordered_map(f, l, n, hasher(), key_equal(), a) { } template<class InputIterator> constexpr unordered_map(InputIterator f, InputIterator l, size_type n, const hasher& hf, const allocator_type& a) : unordered_map(f, l, n, hf, key_equal(), a) { } template<container-compatible-range<value_type> R> constexpr unordered_map(from_range_t, R&& rg, size_type n, const allocator_type& a) : unordered_map(from_range, std::forward<R>(rg), n, hasher(), key_equal(), a) { } template<container-compatible-range<value_type> R> constexpr unordered_map(from_range_t, R&& rg, size_type n, const hasher& hf, const allocator_type& a) : unordered_map(from_range, std::forward<R>(rg), n, hf, key_equal(), a) { } constexpr unordered_map(initializer_list<value_type> il, size_type n, const allocator_type& a) : unordered_map(il, n, hasher(), key_equal(), a) { } constexpr unordered_map(initializer_list<value_type> il, size_type n, const hasher& hf, const allocator_type& a) : unordered_map(il, n, hf, key_equal(), a) { } constexpr ~unordered_map(); constexpr unordered_map& operator=(const unordered_map&); constexpr unordered_map& operator=(unordered_map&&) noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_move_assignable_v<Hash> && is_nothrow_move_assignable_v<Pred>); constexpr unordered_map& operator=(initializer_list<value_type>); constexpr allocator_type get_allocator() const noexcept; // iterators constexpr iterator begin() noexcept; constexpr const_iterator begin() const noexcept; constexpr iterator end() noexcept; constexpr const_iterator end() const noexcept; constexpr const_iterator cbegin() const noexcept; constexpr const_iterator cend() const noexcept; // capacity constexpr bool empty() const noexcept; constexpr size_type size() const noexcept; constexpr size_type max_size() const noexcept; // [unord.map.modifiers], modifiers template<class... Args> constexpr pair<iterator, bool> emplace(Args&&... args); template<class... Args> constexpr iterator emplace_hint(const_iterator position, Args&&... args); constexpr pair<iterator, bool> insert(const value_type& obj); constexpr pair<iterator, bool> insert(value_type&& obj); template<class P> constexpr pair<iterator, bool> insert(P&& obj); constexpr iterator insert(const_iterator hint, const value_type& obj); constexpr iterator insert(const_iterator hint, value_type&& obj); template<class P> constexpr iterator insert(const_iterator hint, P&& obj); template<class InputIterator> constexpr void insert(InputIterator first, InputIterator last); template<container-compatible-range<value_type> R> constexpr void insert_range(R&& rg); constexpr void insert(initializer_list<value_type>); constexpr node_type extract(const_iterator position); constexpr node_type extract(const key_type& x); template<class K> constexpr node_type extract(K&& x); constexpr insert_return_type insert(node_type&& nh); constexpr iterator insert(const_iterator hint, node_type&& nh); template<class... Args> constexpr pair<iterator, bool> try_emplace(const key_type& k, Args&&... args); template<class... Args> constexpr pair<iterator, bool> try_emplace(key_type&& k, Args&&... args); template<class K, class... Args> constexpr pair<iterator, bool> try_emplace(K&& k, Args&&... args); template<class... Args> constexpr iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args); template<class... Args> constexpr iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args); template<class K, class... Args> constexpr iterator try_emplace(const_iterator hint, K&& k, Args&&... args); template<class M> constexpr pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj); template<class M> constexpr pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template<class K, class M> constexpr pair<iterator, bool> insert_or_assign(K&& k, M&& obj); template<class M> constexpr iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj); template<class M> constexpr iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj); template<class K, class M> constexpr iterator insert_or_assign(const_iterator hint, K&& k, M&& obj); constexpr iterator erase(iterator position); constexpr iterator erase(const_iterator position); constexpr size_type erase(const key_type& k); template<class K> constexpr size_type erase(K&& x); constexpr iterator erase(const_iterator first, const_iterator last); constexpr void swap(unordered_map&) noexcept(allocator_traits<Allocator>::is_always_equal::value && is_nothrow_swappable_v<Hash> && is_nothrow_swappable_v<Pred>); constexpr void clear() noexcept; template<class H2, class P2> constexpr void merge(unordered_map<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> constexpr void merge(unordered_map<Key, T, H2, P2, Allocator>&& source); template<class H2, class P2> constexpr void merge(unordered_multimap<Key, T, H2, P2, Allocator>& source); template<class H2, class P2> constexpr void merge(unordered_multimap<Key, T, H2, P2, Allocator>&& source); // observers constexpr hasher hash_function() const; constexpr key_equal key_eq() const; // map operations constexpr iterator find(const key_type& k); constexpr const_iterator find(const key_type& k) const; template<class K> constexpr iterator find(const K& k); template<class K> constexpr const_iterator find(const K& k) const; constexpr size_type count(const key_type& k) const; template<class K> constexpr size_type count(const K& k) const; constexpr bool contains(const key_type& k) const; template<class K> constexpr bool contains(const K& k) const; constexpr pair<iterator, iterator> equal_range(const key_type& k); constexpr pair<const_iterator, const_iterator> equal_range(const key_type& k) const; template<class K> constexpr pair<iterator, iterator> equal_range(const K& k); template<class K> constexpr pair<const_iterator, const_iterator> equal_range(const K& k) const; // [unord.map.elem], element access constexpr mapped_type& operator[](const key_type& k); constexpr mapped_type& operator[](key_type&& k); template<class K> constexpr mapped_type& operator[](K&& k); constexpr mapped_type& at(const key_type& k); constexpr const mapped_type& at(const key_type& k) const; template<class K> constexpr mapped_type& at(const K& k); template<class K> constexpr const mapped_type& at(const K& k) const; // bucket interface constexpr size_type bucket_count() const noexcept; constexpr size_type max_bucket_count() const noexcept; constexpr size_type bucket_size(size_type n) const; constexpr size_type bucket(const key_type& k) const; template<class K> constexpr size_type bucket(const K& k) const; constexpr local_iterator begin(size_type n); constexpr const_local_iterator begin(size_type n) const; constexpr local_iterator end(size_type n); constexpr const_local_iterator end(size_type n) const; constexpr const_local_iterator cbegin(size_type n) const; constexpr const_local_iterator cend(size_type n) const; // hash policy constexpr float load_factor() const noexcept; constexpr float max_load_factor() const noexcept; constexpr void max_load_factor(float z); constexpr void rehash(size_type n); constexpr void reserve(size_type n); }; template<class InputIterator, class Hash = hash<iter-key-type<InputIterator>>, class Pred = equal_to<iter-key-type<InputIterator>>, class Allocator = allocator<iter-to-alloc-type<InputIterator>>> unordered_map(InputIterator, InputIterator, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, Pred, Allocator>; template<ranges::input_range R, class Hash = hash<range-key-type<R>>, class Pred = equal_to<range-key-type<R>>, class Allocator = allocator<range-to-alloc-type<R>>> unordered_map(from_range_t, R&&, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_map<range-key-type<R>, range-mapped-type<R>, Hash, Pred, Allocator>; template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>> unordered_map(initializer_list<pair<Key, T>>, typename see below::size_type = see below, Hash = Hash(), Pred = Pred(), Allocator = Allocator()) -> unordered_map<Key, T, Hash, Pred, Allocator>; template<class InputIterator, class Allocator> unordered_map(InputIterator, InputIterator, typename see below::size_type, Allocator) -> unordered_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, hash<iter-key-type<InputIterator>>, equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Allocator> unordered_map(InputIterator, InputIterator, Allocator) -> unordered_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, hash<iter-key-type<InputIterator>>, equal_to<iter-key-type<InputIterator>>, Allocator>; template<class InputIterator, class Hash, class Allocator> unordered_map(InputIterator, InputIterator, typename see below::size_type, Hash, Allocator) -> unordered_map<iter-key-type<InputIterator>, iter-mapped-type<InputIterator>, Hash, equal_to<iter-key-type<InputIterator>>, Allocator>; template<ranges::input_range R, class Allocator> unordered_map(from_range_t, R&&, typename see below::size_type, Allocator) -> unordered_map<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>, equal_to<range-key-type<R>>, Allocator>; template<ranges::input_range R, class Allocator> unordered_map(from_range_t, R&&, Allocator) -> unordered_map<range-key-type<R>, range-mapped-type<R>, hash<range-key-type<R>>, equal_to<range-key-type<R>>, Allocator>; template<ranges::input_range R, class Hash, class Allocator> unordered_map(from_range_t, R&&, typename see below::size_type, Hash, Allocator) -> unordered_map<range-key-type<R>, range-mapped-type<R>, Hash, equal_to<range-key-type<R>>, Allocator>; template<class Key, class T, class Allocator> unordered_map(initializer_list<pair<Key, T>>, typename see below::size_type, Allocator) -> unordered_map<Key, T, hash<Key>, equal_to<Key>, Allocator>; template<class Key, class T, class Allocator> unordered_map(initializer_list<pair<Key, T>>, Allocator) -> unordered_map<Key, T, hash<Key>, equal_to<Key>, Allocator>; template<class Key, class T, class Hash, class Allocator> unordered_map(initializer_list<pair<Key, T>>, typename see below::size_type, Hash, Allocator) -> unordered_map<Key, T, Hash, equal_to<Key>, Allocator>; }
A size_type parameter type in an unordered_map deduction guide refers to the size_type member type of the type deduced by the deduction guide.

24.5.4.2 Constructors [unord.map.cnstr]

constexpr unordered_map() : unordered_map(size_type(see below)) { } constexpr explicit unordered_map(size_type n, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());
Effects: Constructs an empty unordered_map using the specified hash function, key equality predicate, and allocator, and using at least n buckets.
For the default constructor, the number of buckets is implementation-defined.
max_load_factor() returns 1.0.
Complexity: Constant.
template<class InputIterator> constexpr unordered_map(InputIterator f, InputIterator l, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); template<container-compatible-range<value_type> R> constexpr unordered_map(from_range_t, R&& rg, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type()); constexpr unordered_map(initializer_list<value_type> il, size_type n = see below, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& a = allocator_type());
Effects: Constructs an empty unordered_map using the specified hash function, key equality predicate, and allocator, and using at least n buckets.
If n is not provided, the number of buckets is implementation-defined.
Then inserts elements from the range [f, l), rg, or il, respectively.
max_load_factor() returns 1.0.
Complexity: Average case linear, worst case quadratic.

24.5.4.3 Element access [unord.map.elem]

constexpr mapped_type& operator[](const key_type& k);
Effects: Equivalent to: return try_emplace(k).first->second;
constexpr mapped_type& operator[](key_type&& k);
Effects: Equivalent to: return try_emplace(std​::​move(k)).first->second;
template<class K> constexpr mapped_type& operator[](K&& k);
Constraints: The qualified-ids Hash​::​is_transparent and Pred​::​is_transparent are valid and denote types.
Effects: Equivalent to: return try_emplace(std​::​forward<K>(k)).first->second;
constexpr mapped_type& at(const key_type& k); constexpr const mapped_type& at(const key_type& k) const;
Returns: A reference to x.second, where x is the (unique) element whose key is equivalent to k.
Throws: An exception object of type out_of_range if no such element is present.
template<class K> constexpr mapped_type& at(const K& k); template<class K> constexpr const mapped_type& at(const K& k) const;
Constraints: The qualified-ids Hash​::​is_transparent and Pred​::​is_transparent are valid and denote types.
Preconditions: The expression find(k) is well-formed and has well-defined behavior.
Returns: A reference to find(k)->second.
Throws: An exception object of type out_of_range if find(k) == end() is true.

24.5.4.4 Modifiers [unord.map.modifiers]

template<class P> constexpr pair<iterator, bool> insert(P&& obj);
Constraints: is_constructible_v<value_type, P&&> is true.
Effects: Equivalent to: return emplace(std​::​forward<P>(obj));
template<class P> constexpr iterator insert(const_iterator hint, P&& obj);
Constraints: is_constructible_v<value_type, P&&> is true.
Effects: Equivalent to: return emplace_hint(hint, std​::​forward<P>(obj));
template<class... Args> constexpr pair<iterator, bool> try_emplace(const key_type& k, Args&&... args); template<class... Args> constexpr iterator try_emplace(const_iterator hint, const key_type& k, Args&&... args);
Preconditions: value_type is Cpp17EmplaceConstructible into unordered_map from piecewise_construct, forward_as_tuple(k), forward_as_tuple(std​::​forward<Args>(args)...).
Effects: If the map already contains an element whose key is equivalent to k, there is no effect.
Otherwise inserts an object of type value_type constructed with piecewise_construct, forward_as_tuple(k), forward_as_tuple(std​::​forward<Args>(args)...).
Returns: In the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.
template<class... Args> constexpr pair<iterator, bool> try_emplace(key_type&& k, Args&&... args); template<class... Args> constexpr iterator try_emplace(const_iterator hint, key_type&& k, Args&&... args);
Preconditions: value_type is Cpp17EmplaceConstructible into unordered_map from piecewise_construct, forward_as_tuple(std​::​move(k)), forward_as_tuple(std​::​forward<Args>(args)...).
Effects: If the map already contains an element whose key is equivalent to k, there is no effect.
Otherwise inserts an object of type value_type constructed with piecewise_construct, forward_as_tuple(std​::​move(k)), forward_as_tuple(std​::​forward<Args>(args)...).
Returns: In the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.
template<class K, class... Args> constexpr pair<iterator, bool> try_emplace(K&& k, Args&&... args); template<class K, class... Args> constexpr iterator try_emplace(const_iterator hint, K&& k, Args&&... args);
Constraints: The qualified-ids Hash​::​is_transparent and Pred​::​is_transparent are valid and denote types.
For the first overload, is_convertible_v<K&&, const_iterator> and is_convertible_v<K&&, iterator> are both false.
Preconditions: value_type is Cpp17EmplaceConstructible into unordered_map from piecewise_construct, forward_as_tuple(std​::​forward<K>(k)), forward_as_tuple(std​::​forward<Args>
(args)...)
.
Effects: If the map already contains an element whose key is equivalent to k, there is no effect.
Otherwise, let h be hash_function()(k).
Constructs an object u of type value_type with piecewise_construct, forward_as_tuple(std​::​forward<K>(k)), forward_as_tuple(std​::​forward<Args>(args)...).

If hash_function()(u.first) != h || contains(u.first) is true, the behavior is undefined.
Inserts u into *this.
Returns: For the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.
template<class M> constexpr pair<iterator, bool> insert_or_assign(const key_type& k, M&& obj); template<class M> constexpr iterator insert_or_assign(const_iterator hint, const key_type& k, M&& obj);
Mandates: is_assignable_v<mapped_type&, M&&> is true.
Preconditions: value_type is Cpp17EmplaceConstructible into unordered_map from k, std​::​forward<M>(obj).
Effects: If the map already contains an element e whose key is equivalent to k, assigns std​::​forward<M>(obj) to e.second.
Otherwise inserts an object of type value_type constructed with k, std​::​forward<M>(obj).
Returns: In the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.
template<class M> constexpr pair<iterator, bool> insert_or_assign(key_type&& k, M&& obj); template<class M> constexpr iterator insert_or_assign(const_iterator hint, key_type&& k, M&& obj);
Mandates: is_assignable_v<mapped_type&, M&&> is true.
Preconditions: value_type is Cpp17EmplaceConstructible into unordered_map from std​::​move(k), std​::​​forward<M>(obj).
Effects: If the map already contains an element e whose key is equivalent to k, assigns std​::​forward<M>(obj) to e.second.
Otherwise inserts an object of type value_type constructed with std​::​​move(k), std​::​forward<M>(obj).
Returns: In the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.
template<class K, class M> constexpr pair<iterator, bool> insert_or_assign(K&& k, M&& obj); template<class K, class M> constexpr iterator insert_or_assign(const_iterator hint, K&& k, M&& obj);
Constraints: The qualified-ids Hash​::​is_transparent and Pred​::​is_transparent are valid and denote types.
Mandates: is_assignable_v<mapped_type&, M&&> is true.
Preconditions: value_type is Cpp17EmplaceConstructible into unordered_map from std​::​forward<K>
(k), std​::​forward<M>(obj)
.
Effects: If the map already contains an element e whose key is equivalent to k, assigns std​::​forward<M>
(obj)
to e.second.
Otherwise, let h be hash_function()(k).
Constructs an object u of type value_type with std​::​forward<K>(k), std​::​forward<M>(obj).
If hash_function()(u.first) != h || contains(u.first) is true, the behavior is undefined.
Inserts u into *this.
Returns: For the first overload, the bool component of the returned pair is true if and only if the insertion took place.
The returned iterator points to the map element whose key is equivalent to k.
Complexity: The same as emplace and emplace_hint, respectively.

24.5.4.5 Erasure [unord.map.erasure]

template<class K, class T, class H, class P, class A, class Predicate> constexpr typename unordered_map<K, T, H, P, A>::size_type erase_if(unordered_map<K, T, H, P, A>& c, Predicate pred);
Effects: Equivalent to: auto original_size = c.size(); for (auto i = c.begin(), last = c.end(); i != last; ) { if (pred(*i)) { i = c.erase(i); } else { ++i; } } return original_size - c.size();

24.5.5 Class template unordered_multimap [unord.multimap]

24.5.5.1 Overview [unord.multimap.overview]

An unordered_multimap is an unordered associative container that supports equivalent keys (an instance of unordered_multimap may contain multiple copies of each key value) and that associates values of another type mapped_type with the keys.
The unordered_multimap class supports forward iterators.
An unordered_multimap meets all of the requirements of a container ([container.reqmts]), of an allocator-aware container ([container.alloc.reqmts]), and of an unordered associative container ([unord.req]).
It provides the operations described in the preceding requirements table for equivalent keys; that is, an unordered_multimap supports the a_eq operations in that table, not the a_uniq operations.
For an unordered_multimap<Key, T> the key_type is Key, the mapped_type is T, and the value_type is pair<const Key, T>.
Subclause [unord.multimap] only describes operations on unordered_multimap that are not described in one of the requirement tables, or for which there is additional semantic information.
The types iterator and const_iterator meet the constexpr iterator requirements ([iterator.requirements.general]).
namespace std { template<class Key, class T, class Hash = hash<Key>, class Pred = equal_to<Key>, class Allocator = allocator<pair<const Key, T>>> class unordered_multimap { public: // types using key_type = Key; using mapped_type = T; using value_type = pair<const Key, T>; using hasher = Hash; using key_equal = Pred; using allocator_type = Allocator; using pointer = typename allocator_traits<Allocator>::pointer; using const_pointer = typename allocator_traits<Allocator>::const_pointer; using reference = value_type&; using const_reference = const value_type&; using size_type = implementation-defined; // see [container.requirements] using difference_type = implementation-defined; // see [container.requirements] using iterator = implementation-defined; // see [container.requirements] using const_iterator = implementation-defined; // see [container.requirements] using local_iterator = implementation-defined; // see [container.requirements] using const_local_iterator = implementation-defined; // see [container.requirements] using node_type = unspecified; // [unord.multimap.cnstr], construct/copy/destroy constexpr unordered_multimap(); constexpr explicit unordered_multimap(size_type n, const hasher& hf = hasher(