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

N3199 - More on noexcept for the General Utilities Library (version 2)

During the Rapperswil meeting the Library Working Group decided to revise the library in terms of no except. This paper presents proposed wording for some of these changes. The paper addresses National Body comments CH 16 and GB 60.

This paper poposes additional changes to those presented in N3148 and N3149. Changes in this paper are restricted to chapter 20 (general utilities library).

Revision history

This paper is a revision of N3157. In this revision changes are targeted to: Other changes previously existing in N3157 are left out of this paper, so they can be discussed in isolation.

Discussion

Utility components

Function swap() is not able to throw if the corresponding move construction is not able to throw and the corresponding move assignmente is not able to throw. This paper makes it conditionally noexcept. The array specialization of swap() can be made noexcept in the cases where individual swap() does not throw.

Functions forward() and move() essentially perform type transformations and therefore cannot throw. The same can be applied tp move_if_noexcept().

For pair and tuple move constructors and move assignments have been made conditionally noexcept. The same has been done to swap().

Bitset

Some constructors of bitset cannot throw. This is the case of the following constructors:
constexpr bitset();
constexpr bitset(unsigned long long val);

Many bitset members do not need to throw. For example, setting a bit should not throw. However, those member functions did not have a throw() specification. The proposal is to make all of them noexcept. This also affects to some free operators (&, | and ^).

However, some operators (<<, >>, <<= and >>=) remain noexcept(false) as the specification does not have any requires elements, and the intro says that generally bound violations may throw exceptions.

Function objects

For the polymorphic function wrapper the following changes have been made:

Memory

Getting the address of a reference through an allocator (allocator<T>::address()) does not throw. Note that it does not make use of operator& which avoids the activation of a user-defined unary operator& which could throw. Thus, address() operation has been made noexcept.

Operations in class raw_storage_iterator should not throw (except assignment). All its members have been made noexcept.

Temporary buffers management functions do not throw. Instead get_temporary_buffer() returns a pair of 0 values if not successful. They have been maded noexcept.

Function swap() for unique_ptr's has been made noexcept.

operator* for unique_ptr and operator[] for array specialization of unique_ptr both are based on get() which does not throw. So both operators have been made noexcept. The same is applied to sepcializations of swap() algorithm (based on non-throwing swap() member function).

In shared_ptr, assignment operators are based on non-throwing swap(). However, only those versions based on non-throwing constructors may be made noexcept. The same can be applied to the first version of reset().

For weak_ptr member function reset() does not throw as it is based on non-throwing swap().

Some functions for atomic access to shared_ptrs (the non-explicit) ones have been made noexcept as they rest on the explicit versions, which are already non-throwing.

Function align does not throw because on failure it does nothing.

Scoped allocator

Scoped allocator operators == has been made noexcept to satisfy Allocator requirements. The same applies to operator != which defined in terms of operator ==.

Because an scoped allocator must meet allocator requirements, its constructors (except de default constructor) cannot throw. Every such constructor has been made noexcept.

Member functions for accessing inner and outer allocators all return references to the required allocator. They have been made noexcept.

Member function deallocate() must be non-throwing because of allocator requirements. It has been made noexcept.

Acknowledgments

I am very grateful to Daniel Krügler and Pablo Halpern for their review and suggestions.

Proposed Wording

20.3 Utility components

After p. 1
#include <initializer_list>
namespace std {
...
  // 20.3.2, swap:
  template<class T> void swap(T& a, T& b) noexcept(see below);
  template<class T, size_t N> void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a,*b)));
...
  // 20.3.3, forward/move:
  template <class T, class U> T&& forward(U&&) noexcept;
  template <class T> typename remove_reference<T>::type&& move(T&&) noexcept;
  template <class T> typename conditional<
    !has_nothrow_move_constructor<T>::value && has_copy_constructor<T>::value,
    const T&, T&&>::type move_if_noexcept(T& x) noexcept;
...
  // 20.3.5.4, tuple-like access to pair:
...
  template<size_t I, class T1, class T2>
    typename tuple_element<I, std::pair<T1, T2> >::type& get(std::pair<T1, T2>&)noexcept;
  template<size_t I, class T1, class T2> const
    typename const tuple_element<I, std::pair<T1, T2> >::type& get(const std::pair<T1, T2>&)noexcept;
...
}

20.3.2 swap

Before p. 1
template<class T> void swap(T& a, T& b) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_move_constructible<T>::value &&
  is_nothrow_move_assignable<T>::value
Before p. 3
template<class T, size_t N>
  void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a,*b)));

20.3.3 forward/move helpers

After p. 1
template <class T, class U> T&& forward(U&& u) noexcept;
After p. 5
template <class T> typename remove_reference<T>::type&& move(T&& t) noexcept;
After p. 8
template <class T> typename conditional<
  !has_nothrow_move_constructor<T>::value && has_copy_constructor<T>::value,
  const T&, T&&>::type move_if_noexcept(T& x) noexcept;

20.3.5.2 Class template pair

Before p. 1
namespace std {
  template <class T1, class T2>
  struct pair {
    typedef T1 first_type;
    typedef T2 second_type;

    T1 first;
    T2 second;
    pair(const pair&) = default;
    constexpr pair();
    pair(const T1& x, const T2& y);
    template<class U, class V> pair(U&& x, V&& y) noexcept(see below);
    template<class U, class V> pair(const pair<U, V>& p);
    template<class U, class V> pair(pair<U, V>&& p) noexcept(see below);
    template <class... Args1, class... Args2>
      pair(piecewise_construct_t,
	   tuple<Args1...> first_args, tuple<Args2...> second_args) noexcept(see below);

    template<class U, class V> pair& operator=(const pair<U, V>& p);
      pair& operator=(pair&& p);
    template<class U, class V> pair& operator=(pair<U, V>&& p) noexcept(see below);

    void swap(pair& p)noexcept(see below);
  };
}
Before p. 3
template<class U, class V> pair(U&& x, V&& y) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<T1, U&&>::value &&
  is_nothrow_constructible<T2, V&&>::value
Before p. 6
template<class U, class V> pair(pair<U, V>&& p) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<T1, U&&>::value &&
  is_nothrow_constructible<T2, V&&>::value
Before p. 7
template<class... Args1, class... Args2>
  pair(piecewise_construct_t,
       tuple<Args1...> first_args, tuple<Args2...> second_args)noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<T1, Args1&&...>::value &&
  is_nothrow_constructible<T2, Args2&&...>::value
Before p. 12
pair& operator=(pair&& p) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_move_assignable<T1>::value &&
  is_nothrow_move_assignable<T2>::value
Before p. 14
template<class U, class V> pair& operator=(pair<U, V>&& p) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_assignable<T1&, U&&>::value &&
  is_nothrow_assignable<T2&, V&&>::value
Before p. 16
void swap(pair& p)noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  noexcept(swap(first, p.first)) &&
  noexcept(swap(second, p.second))

20.3.5.3 Specialized algorithms

After p. 6
template<class T1, class T2> void swap(pair<T1, T2>& x, pair<T1, T2>& y)
  noexcept(noexcept(x.swap(y)));
After p. 7
template <class T1, class T2>
  pair<V1, V2> make_pair(T1&&, T2&&) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<V1, T1&&>::value &&
  is_nothrow_constructible<V2, T2&&>::value

20.3.5.4 Tuple-like access to pair

After p. 4
template<size_t I, class T1, class T2>
  typename tuple_element<I, std::pair<T1, T2> >::type& get(pair<T1, T2>&) noexcept;
template<size_t I, class T1, class T2>
  const typename tuple_element<I, std::pair<T1, T2> >::type& get(const pair<T1, T2>&) noexcept;

20.4.1 In general

After p. 2
namespace std {
...
  // 20.4.2.4, tuple creation functions:
...
  template <class... Types>
    tuple<ATypes...> pack_arguments forward_as_tuple(Types&&...) noexcept;
  template<class... Types>
    tuple<Types&...> tie(Types&...) noexcept;
...
}

20.4.2 Class template tuple

Before p. 1
namespace std {
  template <class... Types>
  class tuple {
  public:

    // 20.4.2.1, tuple construction
...
    template <class... UTypes>
      explicit tuple(UTypes&&...) noexcept(see below);
...  
    template <class... UTypes>
      tuple(tuple<UTypes...>&&) noexcept(see below);

...
    template <class U1, class U2>
      tuple(pair<U1, U2>&&) noexcept(see below); // iff sizeof...(Types) == 2
...
    // 20.4.2.2, tuple assignment
...
    tuple& operator=(tuple&&) noexcept(see below);
...  
    template <class... UTypes>
      tuple& operator=(tuple<UTypes...>&&) noexcept(see below);
...  
    template <class U1, class U2>
      tuple& operator=(pair<U1, U2>&&) noexcept(see below); // iff sizeof...(Types) == 2
  
    // 20.4.2.3, tuple swap
    void swap(tuple&) noexcept(see below);

  };
}

20.4.2.1 Construction

Before p. 6
template <class... UTypes>
  explicit tuple(UTypes&&... u) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to the logical and of the following expressions:

  is_nothrow_constructible<Ti, Ui&&>::value

where Ti is the i-th type in Types and Ui is the i-th type in UTypes.

Before p. 15
template <class... UTypes> tuple(tuple<UTypes...>&& u) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to the logical and of the following expressions:

  is_nothrow_constructible<Ti, Ui&&>::value

where Ti is the i-th type in Types and Ui is the i-th type in UTypes.

Before p. 19
template <class U1, class U2> tuple(pair<U1, U2>&& u) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_constructible<T1, U1&&>::value &&
  is_nothrow_constructible<T2, U2&&>::value

where Ti is the i-th type in Types.

20.4.2.2 Assignment

Before p. 5
tuple& operator=(tuple&& u) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to the logical and of the following expressions:

  is_nothrow_move_assignable<Ti>::value

where Ti is the i-th type in Types.

Before p. 11
template <class... UTypes>
  tuple& operator=(tuple<UTypes...>&& u) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to the logical and of the following expressions:

  is_nothrow_assignable<Ti&, Ui&&>::value

where Ti is the i-th type in Types and Ui is the i-th type in UTypes.

Before p. 18
template <class U1, class U2> tuple& operator=(pair<U1, U2>&& u) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

  is_nothrow_assignable<T1&, U1&&>::value &&
  is_nothrow_assignable<T2&, U2&&>::value

where Ti is the i-th type in Types.

20.4.2.3 swap

Before p. 1
void swap(tuple& rhs) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to the logical and of the following expressions:

  noexcept(swap(declval<Ti&>(), declval<Ti&>()))

where Ti is the i-th type in Types.

20.4.2.4 Tuple creation functions

Before p. 4
template<class... Types>
  tuple<Types&&...>forward_as_tuple(Types&&... t) noexcept;
Before p. 6
template<class... Types>
  tuple<Types&...> tie(Types&... t) noexcept;

20.4.2.9 Tuple specialized algorithms

Before p. 1
template <class... Types>
  void swap(tuple<Types...>& x, tuple<Types...>& y) noexcept(see below);

Remarks: The expression inside noexcept is equivalent to:

    noexcept(x.swap(y));

20.5 Class template bitset

Before p. 1
#include <string>
#include <iosfwd> // for istream, ostream
namespace std {
  template <size_t N> class bitset;

  // 20.5.4 bitset operators:
  template <size_t N>
    bitset<N> operator&(const bitset<N>&, const bitset<N>&) noexcept;
  template <size_t N>
    bitset<N> operator|(const bitset<N>&, const bitset<N>&) noexcept;
  template <size_t N>
    bitset<N> operator^(const bitset<N>&, const bitset<N>&) noexcept;
  template <class charT, class traits, size_t N>
    basic_istream<charT, traits>&
    operator>>(basic_istream<charT, traits>& is, bitset<N>& x);
  template <class charT, class traits, size_t N>
    basic_ostream<charT, traits>&
    operator<<(basic_ostream<charT, traits>& os, const bitset<N>& x);
}
After p. 1
namespace std {
  template<size_t N> class bitset {
  public:
    // bit reference:
    class reference {
      friend class bitset;
      reference() noexcept;
    public:
      ~reference() noexcept;
      reference& operator=(bool x) noexcept; // for b[i] = x;
      reference& operator=(const reference&) noexcept; // for b[i] = b[j];
      bool operator~() const noexcept; // flips the bit
      operator bool() const noexcept; // for x = b[i];
      reference& flip() noexcept; // for b[i].flip();
    };

    // 20.5.1 constructors:
    constexpr bitset() noexcept;
    constexpr bitset(unsigned long long val) noexcept;
    template<class charT, class traits, class Allocator>
      explicit bitset(
        const basic_string<charT,traits,Allocator>& str,
        typename basic_string<charT,traits,Allocator>::size_type pos = 0,
        typename basic_string<charT,traits,Allocator>::size_type n =
          basic_string<charT,traits,Allocator>::npos,
          charT zero = charT(’0’), charT one = charT(’1’));
    explicit bitset(const char *str);

    // 20.5.2 bitset operations:
    bitset<N>& operator&=(const bitset<N>& rhs) noexcept;
    bitset<N>& operator|=(const bitset<N>& rhs) noexcept;
    bitset<N>& operator^=(const bitset<N>& rhs) noexcept;
...
    bitset<N>& set() noexcept;
    bitset<N>& set(size_t pos, bool val = true);
    bitset<N>& reset() noexcept;
    bitset<N>& reset(size_t pos);
    bitset<N> operator~() const noexcept;
    bitset<N>& flip() noexcept;
    bitset<N>& flip(size_t pos);

    // element access:
    constexpr bool operator[](size_t pos) const; // for b[i];
    reference operator[](size_t pos); // for b[i];

    unsigned long to_ulong() const;
    unsigned long long to_ullong() const;
    template <class charT = char,
        class traits = char_traits<charT>,
        class Allocator = allocator<charT> >
      basic_string<charT, traits, Allocator>
      to_string(charT zero = charT(’0’), charT one = charT(’1’)) const;
    size_t count() const noexcept;
    constexpr size_t size() noexcept;
    bool operator==(const bitset<N>& rhs) const noexcept;
    bool operator!=(const bitset<N>& rhs) const noexcept;
    bool test(size_t pos);
    bool all() const noexcept;
    bool any() const noexcept;
    bool none() const noexcept;
...
  };

  // 20.5.3 Hash support
  template <class T> struct hash;
  template <size_t N> struct hash<bitset<N> >;
}

20.5.1 bitset constructors

Before p. 1
constexpr bitset() noexcept;
After p. 1
constexpr bitset(unsigned long long val) noexcept;

20.5.2 bitset members

Before p. 1
bitset<N>& operator&=(const bitset<N>& rhs) noexcept;
After p. 2
bitset<N>& operator|=(const bitset<N>& rhs) noexcept;
After p. 4
bitset<N>& operator^=(const bitset<N>& rhs) noexcept;
After p. 6
bitset<N>& operator<<=(size_t pos) noexcept;
After p. 8
bitset<N>& operator>>=(size_t pos) noexcept;
After p. 10
bitset<N>& set() noexcept;
After p. 16
bitset<N>& reset() noexcept;
After p. 22
bitset<N> operator~() const noexcept;
After p. 24
bitset<N>& flip() noexcept;
After p. 36
size_t count() const noexcept;
After p. 37
constexpr size_t size() noexcept;
After p. 38
bool operator==(const bitset<N>& rhs) const noexcept;
After p. 39
bool operator!=(const bitset<N>& rhs) const noexcept;
After p. 43
bool all() const noexcept;
After p. 44
bool any() const noexcept;
After p. 45
bool none() const noexcept;
After p. 46
bitset<N> operator<<(size_t pos) const noexcept;
After p. 47
bitset<N> operator>>(size_t pos) const noexcept;

20.5.4 bitset operators

Before p. 1
bitset<N> operator&(const bitset<N>& lhs, const bitset<N>& rhs) noexcept;
After p. 1
bitset<N> operator|(const bitset<N>& lhs, const bitset<N>& rhs) noexcept;
After p. 2
bitset<N> operator^(const bitset<N>& lhs, const bitset<N>& rhs) noexcept;

20.8.14.1 Class bad_function_call

After p. 1
namespace std {
  class bad_function_call : public std::exception {
  public:
    // 20.8.14.1.1, constructor:
    bad_function_call() noexcept;
  };
} // namespace std

20.8.14.1.1 bad_function_call constructor

Before p. 1
bad_function_call() noexcept;

20.9 Memory

After p. 1
namespace std {
...
  // 20.9.8, temporary buffers:
  template <class T>
    pair<T*,ptrdiff_t> get_temporary_buffer(ptrdiff_t n) noexcept;
  template <class T>
    void return_temporary_buffer(T* p) noexcept;
...
    // 20.9.11.5, shared_ptr atomic access:
...
    template<class T>
      shared_ptr<T> atomic_load(const shared_ptr<T>* p) noexcept;
...
    template<class T>
      void atomic_store(shared_ptr<T>* p, shared_ptr<T> r) noexcept;
...
    template<class T>
      shared_ptr<T> atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r) noexcept;
...
    template<class T>
      bool atomic_compare_exchange_weak(
        shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w) noexcept;
    template<class T>
      bool atomic_compare_exchange_strong(
        shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w) noexcept;
...
}

20.9.4 Allocator traits

After p. 1
namespace std {
  template <class Alloc> struct allocator_traits {
...
  static void deallocate(Alloc& a, pointer p, size_type n) noexcept;
...
  };
}

20.9.4.2 Allocator traits static member functions

After p. 2
static void deallocate(Alloc& a, pointer p, size_type n) noexcept;

20.9.5 The default allocator

Before p. 1
namespace std {
  template <class T> class allocator;
...
  template <class T> class allocator {
  public:
...
    pointer address(reference x) const noexcept;
    const_pointer address(const_reference x) const noexcept;
...
    void deallocate(pointer p, size_type n) noexcept;
...
  };
}

20.9.5.1 allocator members

After p. 1
pointer address(reference x) const noexcept;
After p. 2
const_pointer address(const_reference x) const noexcept;
After p. 7
void deallocate(pointer p, size_type n) noexcept;

20.9.7 Raw storage iterator

After p. 1
namespace std {
  template <class OutputIterator, class T>
  class raw_storage_iterator
    : public iterator<output_iterator_tag,void,void,void,void> {
  public:
    explicit raw_storage_iterator(OutputIterator x) noexcept;

    raw_storage_iterator<OutputIterator,T>& operator*() noexcept;
...
    raw_storage_iterator<OutputIterator,T>& operator++() noexcept;
    raw_storage_iterator<OutputIterator,T> operator++(int) noexcept;
  };
}
Before p. 2
raw_storage_iterator(OutputIterator x) noexcept;
After p. 2
raw_storage_iterator<OutputIterator,T>& operator*() noexcept;
After p. 5
raw_storage_iterator<OutputIterator,T>& operator++() noexcept;
After p. 6
raw_storage_iterator<OutputIterator,T> operator++(int) noexcept;

20.9.8 Temporary buffers

Before p. 1
template <class T>
  pair<T*, ptrdiff_t> get_temporary_buffer(ptrdiff_t n) noexcept;
After p. 2
template <class T> void return_temporary_buffer(T* p) noexcept;

20.9.10 Class template unique_ptr

After p. 6
namespace std {
  template<class T> struct default_delete;
  template<class T> struct default_delete<T[]>;

  template<class T, class D = default_delete<T>> class unique_ptr;
  template<class T, class D> class unique_ptr<T[], D>;

  template<class T, class D> void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;

...
}

20.9.10.1.2 default_delete

Before p. 1
namespace std {
  template <class T> struct default_delete {
    constexpr default_delete() noexcept;
    template <class U> default_delete(const default_delete<U>&) noexcept;
    void operator()(T*) const;
  };
}

constexpr default_delete() noexcept;
After p. 1
template <class U> default_delete(const default_delete<U>& other) noexcept;

20.9.10.1.3 default_delete<T[]>

Before p. 1
namespace std {
  template <class T> struct default_delete<T[]> {
    constexpr default_delete() noexcept;
    void operator()(T* ptr) const;
    template  void operator()(U*) const = delete;
  };
}

20.9.10.4 unique_ptr specialized algorithms

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

20.9.11.2 Class template shared_ptr

After p. 1
namespace std {
  template<class T> class shared_ptr {
  public:
...
    // 20.9.11.2.3, assignment:
    shared_ptr& operator=(const shared_ptr& r) noexcept;
    template<class Y> shared_ptr& operator=(const shared_ptr<Y>& r) noexcept;
    shared_ptr& operator=(shared_ptr&& r) noexcept;
    template<class Y> shared_ptr& operator=(shared_ptr<Y>&& r) noexcept;
...
    void reset() noexcept;
...
  };
...
}

20.9.11.2.3 shared_ptr assignment

Before p. 1
shared_ptr& operator=(const shared_ptr& r) noexcept;
template<class Y> shared_ptr& operator=(const shared_ptr<Y>& r) noexcept;
template<class Y> shared_ptr& operator=(auto_ptr<Y>&& r);
After p. 3
shared_ptr& operator=(shared_ptr&& r) noexcept;
template<class Y> shared_ptr& operator=(shared_ptr<Y>&& r) noexcept;

20.9.11.2.4 shared_ptr modifiers

After p. 2
void reset() noexcept;

20.9.11.3 Class template weak_ptr

After p. 1
namespace std {
  template<class T> class weak_ptr {
  public:
...
    void reset() noexcept;
...
  };
}

20.9.11.3.4 weak_ptr modifiers

After p. 2
void reset() noexcept;

20.9.11.5 shared_ptr atomic access

After p. 5
template<class T>
  shared_ptr<T> atomic_load(const shared_ptr<T>* p) noexcept;
After p. 11
template<class T>
  void atomic_store(shared_ptr<T>* p, shared_ptr<T> r) noexcept;
After p. 17
template<class T>
shared_ptr<T> atomic_exchange(shared_ptr<T>* p, shared_ptr<T> r) noexcept;
After p. 23
template<class T>
  bool atomic_compare_exchange_weak(
    shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w) noexcept;
After p. 25
template<class T>
  bool atomic_compare_exchange_strong(
    shared_ptr<T>* p, shared_ptr<T>* v, shared_ptr<T> w) noexcept;

20.9.13 Align

Before p. 1
void *align(std::size_t alignment, std::size_t size,
  void *&ptr, std::size_t& space) noexcept;

20.10 Class scoped_allocator

Modify p. 1
// scoped allocator adaptor
template <class OuterAlloc, class... InnerAlloc>
  class scoped_allocator_adaptor;
template <class OuterA1, class OuterA2, class... InnerAllocs>
  bool operator==(const scoped_allocator_adaptor<OuterA1, InnerAllocs...>& a,)
		  const scoped_allocator_adaptor<OuterA2, InnerAllocs...>& b) noexcept;
template <class OuterA1, class OuterA2, class... InnerAllocs>
  bool operator!=(const scoped_allocator_adaptor<OuterA1, InnerAllocs...>& a,)
		  const scoped_allocator_adaptor<OuterA2, InnerAllocs...>& b) noexcept;
After p. 2
namespace std {
  template <class OuterAlloc, class... InnerAllocs>
    class scoped_allocator_adaptor : public OuterAlloc {
...
    template <class OuterA2>}
      scoped_allocator_adaptor(OuterA2&& outerAlloc,
                               const InnerAllocs&... innerAllocs) noexcept;
    scoped_allocator_adaptor(const scoped_allocator_adaptor& other) noexcept;
    template <class OuterA2>
      scoped_allocator_adaptor(const scoped_allocator_adaptor<OuterA2, InnerAllocs...>& other) noexcept;
    template <class OuterA2>
      scoped_allocator_adaptor(const scoped_allocator_adaptor<OuterA2, InnerAllocs...>&& other) noexcept;
...
    inner_allocator_type& inner_allocator() noexcept;
    const inner_allocator_type& inner_allocator() const noexcept;
    outer_allocator_type& outer_allocator() noexcept;
    const outer_allocator_type& outer_allocator() const noexcept;
...
    void deallocate(pointer p, size_type n) noexcept;
...
  };
template <class OuterA1, class OuterA2, class... InnerAllocs>
  bool operator==(const scoped_allocator_adaptor<OuterA1, InnerAllocs...>& a,
                  const scoped_allocator_adaptor<OuterA2, InnerAllocs...>& b) noexcept;
template <class OuterA1, class OuterA2, class... InnerAllocs>
  bool operator!=(const scoped_allocator_adaptor<OuterA1, InnerAllocs...>& a,
                  const scoped_allocator_adaptor<OuterA2, InnerAllocs...>& b) noexcept;
}

20.10.2 Scoped allocator adaptor constructors

After p. 1
template <class OuterA2>
  scoped_allocator_adaptor(OuterA2&& outerAlloc,
                           const InnerAllocs&... innerAllocs) noexcept;
After p. 3
scoped_allocator_adaptor(const scoped_allocator_adaptor& other) noexcept;
After p. 4
template <class OuterA2>
  scoped_allocator_adaptor(const scoped_allocator_adaptor<OuterA2,
			   InnerAllocs...>& other) noexcept;
After p. 6
template <class OuterA2>
  scoped_allocator_adaptor(const scoped_allocator_adaptor<OuterA2,
			   InnerAllocs...>&& other) noexcept;

20.10.3 Scoped allocator adaptor members

After p. 1
inner_allocator_type& inner_allocator() noexcept;
const inner_allocator_type& inner_allocator() const noexcept;
After p. 2
outer_allocator_type& outer_allocator() noexcept;
After p. 3
const outer_allocator_type& outer_allocator() const noexcept;
After p. 6
void deallocate(pointer p, size_type n) noexcept;

20.13.3 type_index members

Before p. 1
type_index(const type_info& rhs) noexcept;
After p. 1
bool operator==(const type_index& rhs) const noexcept;
After p. 2
bool operator!=(const type_index& rhs) const noexcept;
After p. 3
bool operator<(const type_index& rhs) const noexcept;
After p. 4
bool operator<=(const type_index& rhs) const noexcept;
After p. 5
bool operator>(const type_index& rhs) const noexcept;
After p. 6
bool operator>=(const type_index& rhs) const noexcept;