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).
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().
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.
For the polymorphic function wrapper the following changes have been made:
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 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.
#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; ... }
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>::valueBefore p. 3
template<class T, size_t N> void swap(T (&a)[N], T (&b)[N]) noexcept(noexcept(swap(*a,*b)));
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;
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&&>::valueBefore 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&&>::valueBefore 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&&...>::valueBefore 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>::valueBefore 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&&>::valueBefore 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))
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
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;
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; ... }
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); }; }
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. 15template <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. 19template <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.
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. 11template <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. 18template <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.
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.
template<class... Types> tuple<Types&&...>forward_as_tuple(Types&&... t) noexcept;Before p. 6
template<class... Types> tuple<Types&...> tie(Types&... t) noexcept;
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));
#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> >; }
constexpr bitset() noexcept;After p. 1
constexpr bitset(unsigned long long val) noexcept;
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;
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;
namespace std { class bad_function_call : public std::exception { public: // 20.8.14.1.1, constructor: bad_function_call() noexcept; }; } // namespace std
bad_function_call() noexcept;
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; ... }
namespace std { template <class Alloc> struct allocator_traits { ... static void deallocate(Alloc& a, pointer p, size_type n) noexcept; ... }; }
static void deallocate(Alloc& a, pointer p, size_type n) noexcept;
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; ... }; }
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;
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;
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;
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; ... }
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;
namespace std { template <class T> struct default_delete<T[]> { constexpr default_delete() noexcept; void operator()(T* ptr) const; templatevoid operator()(U*) const = delete; }; }
template <class T, class D> void swap(unique_ptr<T, D>& x, unique_ptr<T, D>& y) noexcept;
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; ... }; ... }
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;
void reset() noexcept;
namespace std { template<class T> class weak_ptr { public: ... void reset() noexcept; ... }; }
void reset() noexcept;
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;
void *align(std::size_t alignment, std::size_t size, void *&ptr, std::size_t& space) noexcept;
// 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,After p. 2)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;
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; }
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;
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;
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;