Document number: N3187=10-0177
Date: 2010-11-26
J. Daniel Garcia
Project: Programming Language C++, Library Working Group
Reply To: josedaniel.garcia@uc3m.es

N3187 - More on noexcept for the Containers Library

This paper studies possible changes to the Containers Library to make broad use of noexcept. The paper addresses National Body comments CH 16 and GB 60.

Changes in this paper are restricted to chapter 23 (containers library).

All changes in this paper are relative to N3126

Discussion

Array

Function swap() on arrays may throw only if the corresponding member function from array may throw. It has been made conditionally noexcept.

Tuple interface functions for class array cannot throw as they return a reference to a position which is known to exist at compile-time.

Member function swap() cannot throw unless the member-wise swap throws an exception (23.3.1.6/2). Thus, it has been made conditionally noexcept.

Iterator functions (begin(), end() and so on) cannot throw in any case (23.2.1/11), because copying an iterator cannot throw.

Member function empty cannot throw. It depends on iterator comparison which cannot throw for library provided iterators. Member function max_size cannot throw as it just computes the size of the largest container.

Operator [] cannot throw as it simply de-references a pointer.

Member function at() has not been made noexcept as it may throw.

Member functions front() and back() have been made noexcept as they simply de-reference a pointer.

Member function data() simply gives access to the internal array returning a pointer and cannot throw

Other sequence containers

All constructors may throw as they are very likely to allocate memory. This is the case for all the member functions imlpying some insertion as assign(), resize(), shrink_to_fit, insert, push_front, and push_back.

All destructors have been made noexcept as they cannot throw per general rules in the library

Member function get_allocator may be made noexcept because any allocator satisfying allocator requirements cannot throw when copying and/or moving. This has been also applied to all iterator acces member functions.

Member function empty cannot throw. It depends on iterator comparison which cannot throw for library provided iterators. Member function max_size cannot throw as it just computes the size of the largest container.

For member function clear application of clause 23.2.1/11 follows that it must be made noexcept.

Function swap() has to consider several factors. First, it shall exchange values without invoking any move, copy or swap on individual elements. Second, if allocator_traits::propagate_on_container_swap::value is true, then allocators from a and b shall be exchanged by means of non-member swap. However, swapping allocators cannot throw. Third, swap() cannot throw for deque as established per 23.2.1/11. Thus, swap has been mad noexcept.

Additional members for vector

Access through operator[] has been made noexcept.

Container Adaptors

For container adaptors most members cannot be made noexcept without providing a condition based on the user provided container. However some members have been made noexcept.

Conditionally noexcept members include move constructors, move assignment operators and swap().

Associative containers

Function swap() on associative containers if the corresponding container swap() member function may throw. It has been made conditionally noexcept.

For member functions, all the reasoning applied to sequence container has been applied here too, with the exception of member function swap(). This member function must be made here conditionally noexcept as it relies on swap() for key comparator.

Open points

Container adaptors do not specifically require that it Container template parameter satisfies the Container requirements. This gives no exception guarantees at all. I think this to be incorrect as it does not provide any semantic guarantee on the container being adapted. However, this is a potentially breaking change that must be carefully analyzed. In the meantime, this paper currently proposes to make swap() member function conditionally noexcept.

Acknowledments

I am very grateful to Daniel Krügler for reviewing this paper.

Proposed Wording

23.2.1 General container requirements [container.requirements.general]

After p.2 add:

? All standard containers will have their destructor's noexcept-specification deduced to noexcept(true).

23.3 Sequence containers [sequences]

After p. 1
namespace std {
  #include <initializer_list>

...
  template <class T, size_t N >
    void swap(array<T,N>& x, array<T,N>& y)noexcept(noexcept(x.swap(y)));

  template <class T> class tuple_size;
  template <size_t I, class T> class tuple_element;
  template <class T, size_t N>
    struct tuple_size<array<T, N> >;
  template <size_t I, class T, size_t N>
    struct tuple_element<I, array<T, N> >;
  template <size_t I, class T, size_t N>
    T& get(array<T, N>&) noexcept;
  template <size_t I, class T, size_t N>
    const T& get(const array<T, N>&) noexcept;
}

23.3.1 Class template array [array]

After p. 3
namespace std {
  template <class T, size_t N >
  struct array {
...

    // No explicit construct/copy/destroy for aggregate type
...
    void swap(array& x) noexcept(noexcept(swap(elems[0], x.elems[0])));

    // iterators:
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const noexcept;
    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;
    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    const_reverse_iterator crend() const noexcept;

    // capacity:
    constexpr size_type size() noexcept;
    constexpr size_type max_size() noexcept;
    constexpr bool empty() noexcept;

    // element access:
    reference operator[](size_type n) noexcept;
    const_reference operator[](size_type n) const noexcept;
    const_reference at(size_type n) const;
    reference at(size_type n);
    reference front() noexcept;
    const_reference front() const noexcept;
    reference back() noexcept;
    const_reference back() const noexcept;
    T * data() noexcept;
    const T * data() const noexcept;
  };
}

23.3.1.2 array specialized algorithms [array.special]

Before p. 1
template <class T, size_t N> void swap(array<T,N>& x, array<T,N>& y)
  noexcept(noexcept(x.swap(y)));

23.3.1.3 array::size [array.size]

Before p. 1
template <class T, size_t N> constexpr size_type array<T,N>::size() noexcept;

23.3.1.4 array::data [array.data]

Before p. 1
T *data() noexcept;
const T *data() const noexcept;

23.3.1.6 array::swap [array.swap]

Before p. 1
void swap(array& y) noexcept(noexcept(swap(declval<T&>(), declval<T&>)));

23.3.1.7 Zero sized arrays [array.zero]

After p.3

? Member function swap() will have a noexcept-specification which is equivalent to noexcept(true).

23.3.1.8 Tuple interface to class template array [array.tuple]

After p. 4
template <size_t I, class T, size_t N> T& get(array<T, N>& a) noexcept;
After p. 7
template <size_t I, class T, size_t N> const T& get(const array<T, N>& a) noexcept;

23.3.2 Class template deque [deque]

After p. 2
namespace std {
  template <class T, class Allocator = allocator<T> >
  class deque {
    public:
...
    allocator_type get_allocator() const noexcept;
    
    // iterators:
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const noexcept;
    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;
    
    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    const_reverse_iterator crend() const noexcept;
    
    // 23.3.2.2 capacity:
    size_type size() const noexcept;
    size_type max_size() const noexcept;
    void resize(size_type sz);
    void resize(size_type sz, const T& c);
    void shrink_to_fit();
    bool empty() const noexcept;
    
    // element access:
    reference operator[](size_type n) noexcept;
    const_reference operator[](size_type n) const noexcept;
...
    
    // 23.3.2.3 modifiers:
...
    void pop_front() noexcept;
    void pop_back() noexcept;
...
    void swap(deque<T,Allocator>&) noexcept;
    void clear() noexcept;
};

...
  // specialized algorithms:
  template <class T, class Allocator>
    void swap(deque<T,Allocator>& x, deque<T,Allocator>& y) noexcept;
}

23.3.2.4 deque specialized algorithms [deque.special]

Before p. 1
template <class T, class Allocator>
  void swap(deque<T,Allocator>& x, deque<T,Allocator>& y) noexcept;

23.3.3 Class template forward_list [forwardlist]

After p. 3
namespace std {
  template <class T, class Allocator = allocator<T> >
  class forward_list {
...
    allocator_type get_allocator() const noexcept;
...
    // 23.3.3.2 iterators:
    iterator before_begin() noexcept;
    const_iterator before_begin() const noexcept;
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;

    const_iterator cbegin() const noexcept;
    const_iterator cbefore_begin() const noexcept;
    const_iterator cend() const noexcept;

    // capacity:
    bool empty() const noexcept;
    size_type max_size() const noexcept;

...
    // 23.3.3.4 modifiers:
    template <class... Args> void emplace_front(Args&&... args);
    void push_front(const T& x);
    void push_front(T&& x);
    void pop_front() noexcept;
...
    void swap(forward_list<T,Allocator>&) noexcept;

    void resize(size_type sz);
    void resize(size_type sz, value_type c);
    void clear() noexcept;
...
  };

...
  // 23.3.3.6 specialized algorithms:
  template <class T, class Allocator>
    void swap(forward_list<T,Allocator>& x, forward_list<T,Allocator>& y) noexcept;

}

23.3.3.2 forward_list iterators [forwardlist.iter]

Before p. 1
iterator before_begin() noexcept;
const_iterator before_begin() const noexcept;
const_iterator cbefore_begin() const noexcept;

23.3.3.4 forward_list modifiers [forwardlist.modifiers]

After p. 3
void pop_front() noexcept;
After p. 27
void clear() noexcept;

23.3.3.6 forward_list specialized algorithms [forwardlist.spec]

Before p. 1
template <class T, class Allocator>
  void swap(forward_list<T,Allocator>& x, forward_list<T,Allocator>& y) noexcept;

23.3.4 Class template list [list]

After p. 3
namespace std {
  template <class T, class Allocator = allocator<T> >
  class list {
  public:
...
    allocator_type get_allocator() const noexcept;

    // iterators:
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const noexcept;
    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;
    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    const_reverse_iterator crend() const noexcept;

    // 23.3.4.2 capacity:
    bool empty() const noexcept;
    size_type size() const noexcept;
    size_type max_size() const noexcept;
    void resize(size_type sz);
    void resize(size_type sz, const T& c);
...
    // 23.3.4.3 modifiers:
...
    void swap(list<T,Allocator>&) noexcept;
    void clear() noexcept;
...
  };
...
  // specialized algorithms:
  template <class T, class Allocator>
    void swap(list<T,Allocator>& x, list<T,Allocator>& y) noexcept;

}

23.3.4.5 list specialized algorithms [list.special]

Before p. 1
template <class T, class Allocator>
  void swap(list<T,Allocator>& x, list<T,Allocator>& y) noexcept;

23.4.1 Class template vector [vector]

After p. 3
namespace std {
  template <class T, class Allocator = allocator<T> >
  class vector {
    public:
...
    allocator_type get_allocator() const noexcept;

    // iterators:
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const noexcept;
    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;

    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    const_reverse_iterator crend() const noexcept;

    // 23.4.1.2 capacity:
    size_type size() const noexcept;
    size_type max_size() const noexcept;
    void resize(size_type sz);
    void resize(size_type sz, const T& c);
    size_type capacity() const noexcept;
    bool empty() const noexcept;
    void reserve(size_type n);
    void shrink_to_fit();

    // element access:
    reference operator[](size_type n) noexcept;
    const_reference operator[](size_type n) const noexcept;
    const_reference at(size_type n) const;
    reference at(size_type n);
...
    // 23.4.1.4 modifiers:
...
    void pop_back() noexcept;
...
    void swap(vector<T,Allocator>&) noexcept;
    void clear() noexcept;
  };
...
  // specialized algorithms:
  template <class T, class Allocator>
    void swap(vector<T,Allocator>& x, vector<T,Allocator>& y) noexcept;

}

23.4.1.2 vector capacity [vector.capacity]

Before p. 1
size_type capacity() const noexcept;
After p. 6
void swap(vector<T,Allocator>& x) noexcept;

23.4.1.5 vector specialized algorithms [vector.special]

Before p. 1
template <class T, class Allocator>
  void swap(vector<T,Allocator>& x, vector<T,Allocator>& y) noexcept;

23.4.2 Class vector [vector.bool]

After p. 1
namespace std {
  template <class Allocator> class vector<bool, Allocator> {
  public:
...
    // bit reference:
    class reference {
      friend class vector;
      reference() noexcept;
    public:
      ~reference() noexcept;
      operator bool() const noexcept;
      reference& operator=(const bool x) noexcept;
      reference& operator=(const reference& x) noexcept;
      void flip() noexcept; // flips the bit
    };

...
    allocator_type get_allocator() const noexcept;

    // iterators:
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const noexcept;
    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;

    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    const_reverse_iterator crend() const noexcept;

    // capacity:
    size_type size() const noexcept;
    size_type max_size() const noexcept;
    void resize(size_type sz, bool c = false);
    size_type capacity() const noexcept;
    bool empty() const noexcept;
    void reserve(size_type n);
    void shrink_to_fit();

   // element access:
    reference operator[](size_type n) noexcept;
    const_reference operator[](size_type n) const noexcept;
    const_reference at(size_type n) const;
    reference at(size_type n);
...

    // modifiers:
...
    void pop_back() noexcept;
...
    iterator erase(const_iterator position) noexcept;
    iterator erase(const_iterator first, const_iterator last) noexcept;
    void swap(vector<T,Allocator>&) noexcept;
    static void swap(reference x, reference y) noexcept;
    void flip() noexcept; // flips all bits
    void clear() noexcept;
  };
After p. 4
void flip() noexcept;
After p. 5
static void swap(reference x, reference y) noexcept;

23.5.1.1 queue definition [queue.defn]

namespace std {
  template <class T, class Container = deque<T> >
  class queue {
  public:
...
    explicit queue(Container&& = Container()) noexcept(see below);
    queue(queue&& q) noexcept(see below);
...
    template <class Alloc> queue(Container&&, const Alloc&) noexcept(see below);
...
    template <class Alloc> queue(queue&&, const Alloc&) noexcept(see below);
    queue& operator=(queue&& q) noexcept(see below);
...    
    void swap(queue& q) noxcept(noexcept(c.swap(q.c)))
      { c.swap(q.c); }
  };
...
  template <class T, class Container>
    void swap(queue<T, Container>& x, queue<T, Container>& y)
      noexcept(noexcept(x.swap(y)));
...
}

23.5.1.2 queue constructors [queue.cons]

After p. 1
explicit queue(Container&& cont = Container()) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_move_constructible<Container>::value
After p. 2
queue(queue&& q) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<Container, Container&&>::value
After p. 3
queue& operator=(queue&& q) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  has_nothrow_move_assign<Container>::value

23.5.1.3 queue constructors with allocators [queue.cons.alloc]

After p. 3
template <class Alloc>
  queue(container_type&& cont, const Alloc& a) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<Container, Container&&, const Alloc&>::value
After p. 5
template <class Alloc>
  queue(queue&& q, const Alloc& a) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<Container, Container&&, const Alloc&>::value

23.5.1.5 queue specialized algorithms [queue.special]

Before p. 1
template <class T, class Container>
  void swap(queue<T, Container>& x, queue<T, Container>& y)noexcept(noexcept(x.swap(y)));

23.5.2 Class template priority_queue [priority.queue]

After p. 1
namespace std {
  template <class T, class Container = vector<T>,
    class Compare = less<typename Container::value_type> >
  class priority_queue {
  public:
...
    explicit priority_queue(const Compare& x = Compare(), Container&& = Container()) noexcept(see below);
...
    priority_queue(priority_queue&&) noexcept(see below);
...
    template <class Alloc> priority_queue(const Compare&,
                                          Container&&, const Alloc&) noexcept(see below);
...
    template <class Alloc> priority_queue(priority_queue&&, const Alloc&) noexcept(see below);
    priority_queue& operator=(priority_queue&&) noexcept(see below);
...
    void swap(priority_queue& q) noexcept(
      noexcept(c.swap(q.c)) && 
      noexcept(comp.swap(q.comp)));
  };
  // no equality is provided
  template <class T, class Container, class Compare>
    void swap(priority_queue<T, Container, Compare>& x, priority_queue<T, Container, Compare>& y)
      noexcept(noexcept(x.swap(y)));
...
}

23.5.2.1 priority_queue constructors [priqueue.cons]

Before p. 1
priority_queue(const Compare& x,
	       const Container& y);
explicit priority_queue(const Compare& x = Compare(),
                        Container&& y = Container()) noexcept(see below);

Remarks: The expression inside noexcept in the second constructor is equivalent to:

  is_nothrow_copy_constructible<Compare>::value &&
  is_nothrow_move_constructible<Container>::value &&
  noexcept(make_heap(y.begin(), y.end(), x))
After p. 4
priority_queue(priority_queue&& q) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_move_constructible<Compare>::value &&
  is_notrhow_move_constructible<Container>::value
After p. 5
priority_queue& operator=(priority_queue&& q) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_move_assignable<Compare>::value &&
  is_notrhow_move_assignable<Container>::value

23.5.2.2 priority_queue constructors with allocators [priqueue.cons.alloc]

After p. 4
template <class Alloc>
  priority_queue(const Compare& compare, Container&& cont, const Alloc& a);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<Container, Container&&, const Alloc&>::value &&
  is_nothrow_copy_constructible<Compare>::value

23.5.2.4 priority_queue specialized algorithms [priqueue.special]

Before p. 1
template <class T, class Container, Compare>
  void swap(priority_queue<T, Container, Compare>& x, priority_queue<T, Container, Compare>& y)
    noexcept(noexcept(x.swap(y)));

23.5.3.1 stack definition [stack.defn]

namespace std {
  template <class T, class Container = deque<T> >
  class stack {
  public:
...
    explicit stack(Container&& = Container());
    stack(stack&&s) noexcept(see below);
...
    template <class Alloc> stack(Container&&, const Alloc&) noexcept(see below);
...
    template <class Alloc> stack(stack&&, const Alloc&) noexcept(see below);
    stack& operator=(stack&& s) noexcept(see below);
...
    void swap(stack& s) noexcept(noexcept(c.swap(s.c))) { c.swap(s.c); }
  };
...
  template <class T, class Container, class Alloc>
    struct uses_allocator<stack<T, Container>, Alloc>
      : uses_allocator<Container, Alloc>::type { };
}

23.5.3.2 stack constructors [stack.cons]

Before p. 1
stack(stack&& s) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_move_constructible<Container>::value
After p. 1
stack& operator=(stack&& s) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_move_constructible<Container>::value

23.5.3.3 stack constructors with allocators [stack.cons.alloc]

After p. 3
template <class Alloc>
  stack(container_type&& cont, const Alloc& a) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<Container, Container&&, const Alloc&>::value
After p. 5
template <class Alloc>
  stack(stack&& s, const Alloc& a) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<Container, Container&&, const Alloc&>::value

23.5.3.5 stack specialized algorithms [stack.special]

Before p. 1
template <class T, class Container>
  void swap(stack<T, Container>& x, stack<T, Container>& y)noexcept(noexcept(x.swap(y)));

23.6 Associative containers [associative]

After p. 1

Header <map> synopsis

namespace std {
...
template <class Key, class T, class Compare, class Allocator>
  void swap(map<Key,T,Compare,Allocator>& x,
            map<Key,T,Compare,Allocator>& y) noexcept(see below);
...
template <class Key, class T, class Compare, class Allocator>
  void swap(multimap<Key,T,Compare,Allocator>& x,
	    multimap<Key,T,Compare,Allocator>& y) noexcept(see below);
}

Header <set> synopsis

namespace std {
...
template <class Key, class Compare, class Allocator>
  void swap(set<Key,Compare,Allocator>& x,
            set<Key,Compare,Allocator>& y) noexcept(see below);
...
template <class Key, class Compare, class Allocator>
  void swap(multiset<Key,Compare,Allocator>& x,
            multiset<Key,Compare,Allocator>& y) noexcept(see below);
}

23.6.1 Class template map [map]

After p. 2
namespace std {
  template <class Key, class T, class Compare = less<Key>,
            class Allocator = allocator<pair<const Key, T> > >
  class map {
  public:
...
    // 23.6.1.1 construct/copy/destroy:
...
    allocator_type get_allocator() const noexcept;

    // iterators:
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;

    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const noexcept;
    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;

    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    const_reverse_iterator crend() const noexcept;

    // capacity:
    bool empty() const noexcept;
    size_type size() const noexcept;
    size_type max_size() const noexcept;
...
// modifiers:
...
    void swap(map<Key,T,Compare,Allocator>& m)
      noexcept(noexcept(swap(declval<key_compare&>(), declval<key_compare&>())));
    void clear() noexcept;
...
  };
...
  // specialized algorithms:
  template <class Key, class T, class Compare, class Allocator>
    void swap(map<Key,T,Compare,Allocator>& x,
	      map<Key,T,Compare,Allocator>& y) noexcept(noexcept(x.swap(y)));

23.6.1.5 map specialized algorithms [map.special]

Before p. 1
template <class Key, class T, class Compare, class Allocator>
  void swap(map<Key,T,Compare,Allocator>& x,
            map<Key,T,Compare,Allocator>& y)noexcept(noexcept(x.swap(y)));

23.6.2 Class template multimap [multimap]

After p. 2
namespace std {
  template <class Key, class T, class Compare = less<Key>,
            class Allocator = allocator<pair<const Key, T> > >
  class multimap {
  public:
...
    allocator_type get_allocator() const noexcept;

    // iterators:
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;

    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const noexcept;
    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;

    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    const_reverse_iterator crend() const noexcept;

    // capacity:
    bool empty() const noexcept;
    size_type size() const noexcept;
    size_type max_size() const noexcept;
...
    // modifiers:
...
    void swap(multimap<Key,T,Compare,Allocator>&)
      noexcept(noexcept(swap(declval<key_compare&>(),declval<key_compare&>())));
    void clear() noexcept;
...
  };

...
  // specialized algorithms:
  template <class Key, class T, class Compare, class Allocator>
    void swap(multimap<Key,T,Compare,Allocator>& x,
	      multimap<Key,T,Compare,Allocator>& y) noexcept(noexcept(x.swap(y)));
}

23.6.2.4 multimap specialized algorithms [multimap.special]

Before p. 1
template <class Key, class T, class Compare, class Allocator>
  void swap(multimap<Key,T,Compare,Allocator>& x,
            multimap<Key,T,Compare,Allocator>& y) noexcept(noexcept(x.swap(y)));

23.6.3 Class template set [set]

After p. 2
namespace std {
  template <class Key, class Compare = less<Key>,
            class Allocator = allocator<Key> >
  class set {
  public:
...
    allocator_type get_allocator() const noexcept;

    // iterators:
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;

    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const noexcept;
    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;

    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    const_reverse_iterator crend() const noexcept;

    // capacity:
    bool empty() const noexcept;
    size_type size() const noexcept;
    size_type max_size() const noexcept;

    // modifiers:
...
    void swap(set<Key,Compare,Allocator>&)
      noexcept(noexcept(swap(declval<key_compare&>(), declval<key_compare&>())));
    void clear() noexcept;
...
  };
...
  // specialized algorithms:
  template <class Key, class Compare, class Allocator>
    void swap(set<Key,Compare,Allocator>& x,
	      set<Key,Compare,Allocator>& y) noexcept(noexcept(x.swap(y)));
}

23.6.3.2 set specialized algorithms [set.special]

Before p. 1
template <class Key, class Compare, class Allocator>
  void swap(set<Key,Compare,Allocator>& x,
	    set<Key,Compare,Allocator>& y) noexcept(noexcept(x.swap(y)));

23.6.4 Class template multiset [multiset]

After p. 2
namespace std {
  template <class Key, class Compare = less<Key>,
            class Allocator = allocator<Key> >
  class multiset {
  public:
...
    allocator_type get_allocator() const noexcept;

    // iterators:
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;

    reverse_iterator rbegin() noexcept;
    const_reverse_iterator rbegin() const noexcept;
    reverse_iterator rend() noexcept;
    const_reverse_iterator rend() const noexcept;

    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;
    const_reverse_iterator crbegin() const noexcept;
    const_reverse_iterator crend() const noexcept;

    // capacity:
    bool empty() const noexcept;
    size_type size() const noexcept;
    size_type max_size() const noexcept;

    // modifiers:
...
    void swap(multiset<Key,Compare,Allocator>&)
      noexcept(noexcept(swap(declval<key_compare&>(), declval<key_compare&>())));
    void clear() noexcept;
...
  };
...
  // specialized algorithms:
  template <class Key, class Compare, class Allocator>
    void swap(multiset<Key,Compare,Allocator>& x,
	      multiset<Key,Compare,Allocator>& y) noexcept(noexcept(x.swap(y)));
}

23.6.4.2 multiset specialized algorithms [multiset.special]

Before p. 1
template <class Key, class Compare, class Allocator>
  void swap(multiset<Key,Compare,Allocator>& x,
	    multiset<Key,Compare,Allocator>& y) noexcept(noexcept(x.swap(y)));

23.7 Unordered associative containers [unord]

After p. 1

Header <unordered_map> synopsis

namespace std {
...
  template <class Key, class T, class Hash, class Pred, class Alloc>
    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>
    void swap(unordered_multimap<Key, T, Hash, Pred, Alloc>& x,
	      unordered_multimap<Key, T, Hash, Pred, Alloc>& y)
    noexcept(noexcept(x.swap(y)));

} // namespace std

Header <unordered_set> synopsis

namespace std {
...
  template <class Key, class Hash, class Pred, class Alloc>
    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>
    void swap(unordered_multiset<Key, Hash, Pred, Alloc>& x,
	      unordered_multiset<Key, Hash, Pred, Alloc>& y)
    noexcept(noexcept(x.swap(y)));
...
} // namespace std

23.7.1 Class template unordered_map [unord.map]

After p. 3
namespace std {
template <class Key,
          class T,
          class Hash = hash<Key>,
          class Pred = std::equal_to<Key>,
          class Alloc = std::allocator<std::pair<const Key, T> > >
  class unordered_map
  {
  public:
...
    allocator_type get_allocator() const noexcept;

    // size and capacity
    bool empty() const noexcept;
    size_type size() const noexcept;
    size_type max_size() const noexcept;

    // iterators
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;

    // modifiers
...
    void clear() noexcept;

    void swap(unordered_map&) noexcept(
      noexcept(swap(declval<hasher&>(),declval<hasher&>())) &&
      noexcept(swap(declval<key_equal&>(), declval<key_equal&>())));
...
    // bucket interface
    size_type bucket_count() const noexcept;
    size_type max_bucket_count() const noexcept;
    size_type bucket_size(size_type n) noexcept;
    size_type bucket(const key_type& k) const noexcept;
    local_iterator begin(size_type n) noexcept;
    const_local_iterator begin(size_type n) const noexcept;
    local_iterator end(size_type n) noexcept;
    const_local_iterator end(size_type n) const noexcept;
    const_local_iterator cbegin(size_type n) const noexcept;
    const_local_iterator cend(size_type n) const noexcept;

    // hash policy
    float load_factor() const noexcept;
    float max_load_factor() const noexcept;
    void max_load_factor(float z) noexcept;
...
  };

template <class Key, class T, class Hash, class Pred, class Alloc>
  void swap(unordered_map<Key, T, Hash, Pred, Alloc>& x,
	    unordered_map<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y)));
}

23.7.1.4 unordered_map swap [unord.map.swap]

Before p. 1
template <class Key, class T, class Hash, class Pred, class Alloc>
  void swap(unordered_map<Key, T, Hash, Pred, Alloc>& x,
	    unordered_map<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y)));

23.7.2 Class template unordered_multimap [unord.multimap]

After p. 3
namespace std {
  template <class Key,
            class T,
            class Hash = hash<Key>,
            class Pred = std::equal_to<Key>,
            class Alloc = std::allocator<std::pair<const Key, T> > >
  class unordered_multimap
  {
  public:
...
    allocator_type get_allocator() const noexcept;

    // size and capacity
    bool empty() const noexcept;
    size_type size() const noexcept;
    size_type max_size() const noexcept;

    // iterators
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;

    // modifiers
...
    void clear() noexcept;

    void swap(unordered_multimap&)noexcept(
      noexcept(swap(declval<hasher&>(),declval<hasher&>())) &&
      noexcept(swap(declval<key_equal&>(), declval<key_equal&>())));
...
    // bucket interface
    size_type bucket_count() const noexcept;
    size_type max_bucket_count() const noexcept;
    size_type bucket_size(size_type n) noexcept;
    size_type bucket(const key_type& k) const noexcept;
    local_iterator begin(size_type n) noexcept;
    const_local_iterator begin(size_type n) const noexcept;
    local_iterator end(size_type n) noexcept;
    const_local_iterator end(size_type n) const noexcept;
    const_local_iterator cbegin(size_type n) const noexcept;
    const_local_iterator cend(size_type n) const noexcept;

    // hash policy
    float load_factor() const noexcept;
    float max_load_factor() const noexcept;
    void max_load_factor(float z) noexcept;
...
  };

  template <class Key, class T, class Hash, class Pred, class Alloc>
    void swap(unordered_multimap<Key, T, Hash, Pred, Alloc>& x,
              unordered_multimap<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y)));
}

23.7.2.3 unordered_multimap swap [unord.multimap.swap]

Before p. 1
template <class Key, class T, class Hash, class Pred, class Alloc>
  void swap(unordered_multimap<Key, T, Hash, Pred, Alloc>& x,
            unordered_multimap<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y)));

23.7.3 Class template unordered_set [unord.set]

After p. 3
namespace std {
  template <class Key,
            class Hash = hash<Key>,
            class Pred = std::equal_to<Key>,
            class Alloc = std::allocator<Key> >
   class unordered_set
  {
  public:
...
    allocator_type get_allocator() const noexcept;

    // size and capacity
    bool empty() const noexcept;
    size_type size() const noexcept;
    size_type max_size() const noexcept;

    // iterators
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;

    // modifiers
...
    void clear() noexcept;

    void swap(unordered_set&)noexcept(
      noexcept(swap(declval<hasher&>(),declval<hasher&>())) &&
      noexcept(swap(declval<key_equal&>(), declval<key_equal&>())));
...
    // bucket interface
    size_type bucket_count() const noexcept;
    size_type max_bucket_count() const noexcept;
    size_type bucket_size(size_type n) noexcept;
    size_type bucket(const key_type& k) const noexcept;
    local_iterator begin(size_type n) noexcept;
    const_local_iterator begin(size_type n) const noexcept;
    local_iterator end(size_type n) noexcept;
    const_local_iterator end(size_type n) const noexcept;
    const_local_iterator cbegin(size_type n) const noexcept;
    const_local_iterator cend(size_type n) const noexcept;

    // hash policy
    float load_factor() const noexcept;
    float max_load_factor() const noexcept;
    void max_load_factor(float z) noexcept;
...
  };

  template <class Key, class T, class Hash, class Pred, class Alloc>
    void swap(unordered_set<Key, T, Hash, Pred, Alloc>& x,
              unordered_set<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y)));
}

23.7.3.2 unordered_set swap [unord.set.swap]

Before p. 1
template <class Key, class Hash, class Pred, class Alloc>
  void swap(unordered_set<Key, Hash, Pred, Alloc>& x,
	    unordered_set<Key, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y)));

23.7.4 Class template unordered_multiset [unord.multiset]

After p. 3
namespace std {
  template ,
            class Pred = std::equal_to,
            class Alloc = std::allocator >
  class unordered_multiset
  {
  public:
...
    allocator_type get_allocator() const noexcept;

    // size and capacity
    bool empty() const noexcept;
    size_type size() const noexcept;
    size_type max_size() const noexcept;

    // iterators
    iterator begin() noexcept;
    const_iterator begin() const noexcept;
    iterator end() noexcept;
    const_iterator end() const noexcept;
    const_iterator cbegin() const noexcept;
    const_iterator cend() const noexcept;

    // modifiers
...
    void clear() noexcept;

    void swap(unordered_multiset&)noexcept(
      noexcept(swap(declval<hasher&>(),declval<hasher&>())) &&
      noexcept(swap(declval<key_equal&>(), declval<key_equal&>())));
...
    // bucket interface
    size_type bucket_count() const noexcept;
    size_type max_bucket_count() const noexcept;
    size_type bucket_size(size_type n) noexcept;
    size_type bucket(const key_type& k) const noexcept;
    local_iterator begin(size_type n) noexcept;
    const_local_iterator begin(size_type n) const noexcept;
    local_iterator end(size_type n) noexcept;
    const_local_iterator end(size_type n) const noexcept;
    const_local_iterator cbegin(size_type n) const noexcept;
    const_local_iterator cend(size_type n) const noexcept;

    // hash policy
    float load_factor() const noexcept;
    float max_load_factor() const noexcept;
    void max_load_factor(float z) noexcept;
...
  };

  template <class Key, class T, class Hash, class Pred, class Alloc>
    void swap(unordered_multiset<Key, T, Hash, Pred, Alloc>& x,
              unordered_multiset<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y)));
}

23.7.4.2 unordered_multiset swap [unord.multiset.swap]

Before p. 1
template <class Key, class T, class Hash, class Pred, class Alloc>
  void swap(unordered_multiset<Key, T, Hash, Pred, Alloc>& x,
            unordered_multiset<Key, T, Hash, Pred, Alloc>& y) noexcept(noexcept(x.swap(y)));