During the Rapperswil meeting the Library Working Group decided to change the empty dynamic exception specifications in the librrary to the noexcept specification in the standard library. There was unanimous consent about this change.
This paper presents proposed wording for this set of changes.
The paper addresses National Body comments CH 16 and GB 60.
I would like to thank Alisdair Meredith for guiding me in the writing of the first version of this paper and providing me with hints to improve it.
Daniel Krügler and David Svoboda provided very useful feedback for the final version of this paper.
... void operator delete(void*)throw()noexcept; void operator delete[](void*)throw()noexcept;
1 Any of the functions defined in the C++ standard library can report a failure by throwing an exception of
a type described in its Throws: paragraph or its exception-specification (15.4). An implementation may
strengthen the exception-specification for a non-virtual function by removing listed exceptions
adding a non-throwing noexcept-specification.195
195) That is, an implementation of the function will have an explicit exception-specification that lists
fewer exceptions than those specified in this International Standard. It may not, however, change the types of exceptions listed
in the exception-specification from those specified, nor add others.
196) That is, the C library functions can all be treated as if they have a throw() exception-specificationare noexcept. This allows implementations
to make performance optimizations based on the absence of exceptions at runtime.
namespace std { template<class T> class numeric_limits { public: static constexpr bool is_specialized = false; static constexpr T min()throw()noexcept { return T(); } static constexpr T max()throw()noexcept { return T(); } static constexpr T lowest()throw()noexcept { return T(); } static constexpr int digits = 0; static constexpr int digits10 = 0; static constexpr int max_digits10 = 0; static constexpr bool is_signed = false; static constexpr bool is_integer = false; static constexpr bool is_exact = false; static constexpr int radix = 0; static constexpr T epsilon()throw()noexcept { return T(); } static constexpr T round_error()throw()noexcept { return T(); } static constexpr int min_exponent = 0; static constexpr int min_exponent10 = 0; static constexpr int max_exponent = 0; static constexpr int max_exponent10 = 0; static constexpr bool has_infinity = false; static constexpr bool has_quiet_NaN = false; static constexpr bool has_signaling_NaN = false; static constexpr float_denorm_style has_denorm = denorm_absent; static constexpr bool has_denorm_loss = false; static constexpr T infinity()throw()noexcept { return T(); } static constexpr T quiet_NaN()throw()noexcept { return T(); } static constexpr T signaling_NaN()throw()noexcept { return T(); } static constexpr T denorm_min()throw()noexcept { return T(); } static constexpr bool is_iec559 = false; static constexpr bool is_bounded = false; static constexpr bool is_modulo = false; static constexpr bool traps = false; static constexpr bool tinyness_before = false; static constexpr float_round_style round_style = round_toward_zero; }; template<class T> class numeric_limits<const T>; template<class T> class numeric_limits<volatile T>; template<class T> class numeric_limits<const volatile T>; }
Before p.1:
static constexpr T min()throw()noexcept;
After p.3:
static constexpr T max()throw()noexcept;
After p.5:
static constexpr T lowest()throw()noexcept;
After p.23:
static constexpr T epsilon()throw()noexcept;
After p.25:
static constexpr T round_error()throw()noexcept;
After p.46:
static constexpr T infinity()throw()noexcept;
After p.48:
static constexpr T quiet_NaN()throw()noexcept;
After p.50:
static constexpr T signaling_NaN()throw()noexcept;
After p.52:
static constexpr T denorm_min()throw()noexcept;
2 [Example:
namespace std { template<> class numeric_limits<float> { public: static constexpr bool is_specialized = true; inline static constexpr float min()throw()noexcept { return 1.17549435E-38F; } inline static constexpr float max()throw()noexcept { return 3.40282347E+38F; } inline static constexpr float lowest()throw()noexcept { return -3.40282347E+38F; } static constexpr int digits = 24; static constexpr int digits10 = 6; static constexpr int max_digits10 = 9; static constexpr bool is_signed = true; static constexpr bool is_integer = false; static constexpr bool is_exact = false; static constexpr int radix = 2; inline static constexpr float epsilon()throw()noexcept { return 1.19209290E-07F; } inline static constexpr float round_error()throw()noexcept { return 0.5F; } static constexpr int min_exponent = -125; static constexpr int min_exponent10 = - 37; static constexpr int max_exponent = +128; static constexpr int max_exponent10 = + 38; static constexpr bool has_infinity = true; static constexpr bool has_quiet_NaN = true; static constexpr bool has_signaling_NaN = true; static constexpr float_denorm_style has_denorm = denorm_absent; static constexpr bool has_denorm_loss = false; inline static constexpr float infinity()throw()noexcept { return ...; } inline static constexpr float quiet_NaN()throw()noexcept { return ...; } inline static constexpr float signaling_NaN()throw()noexcept { return ...; } inline static constexpr float denorm_min()throw()noexcept { return min(); } static constexpr bool is_iec559 = true; static constexpr bool is_bounded = true; static constexpr bool is_modulo = false; static constexpr bool traps = true; static constexpr bool tinyness_before = true; static constexpr float_round_style round_style = round_to_nearest; }; }
-- end example]
3 The specialization for bool shall be provided as follows:
namespace std { template<> class numeric_limits<bool> { public: static constexpr bool is_specialized = true; static constexpr bool min()throw()noexcept { return false; } static constexpr bool max()throw()noexcept { return true; } static constexpr bool lowest()throw()noexcept { return false; } static constexpr int digits = 1; static constexpr int digits10 = 0; static constexpr int max_digits10 = 0; static constexpr bool is_signed = false; static constexpr bool is_integer = true; static constexpr bool is_exact = true; static constexpr int radix = 2; static constexpr bool epsilon()throw()noexcept { return 0; } static constexpr bool round_error()throw()noexcept { return 0; } static constexpr int min_exponent = 0; static constexpr int min_exponent10 = 0; static constexpr int max_exponent = 0; static constexpr int max_exponent10 = 0; static constexpr bool has_infinity = false; static constexpr bool has_quiet_NaN = false; static constexpr bool has_signaling_NaN = false; static constexpr float_denorm_style has_denorm = denorm_absent; static constexpr bool has_denorm_loss = false; static constexpr bool infinity()throw()noexcept { return 0; } static constexpr bool quiet_NaN()throw()noexcept { return 0; } static constexpr bool signaling_NaN()throw()noexcept { return 0; } static constexpr bool denorm_min()throw()noexcept { return 0; } static constexpr bool is_iec559 = false; static constexpr bool is_bounded = true; static constexpr bool is_modulo = false; static constexpr bool traps = false; static constexpr bool tinyness_before = false; static constexpr float_round_style round_style = round_toward_zero; }; }
1 The header <new> defines several functions that manage the allocation of dynamic storage in a program. It also defines components for reporting storage management errors.
namespace std { class bad_alloc; class bad_array_new_length; struct nothrow_t {}; extern const nothrow_t nothrow; typedef void (*new_handler)(); new_handler set_new_handler(new_handler new_p)throw()noexcept; } void* operator new(std::size_t size) throw(std::bad_alloc); void* operator new(std::size_t size, const std::nothrow_t&)throw()noexcept; void operator delete(void* ptr)throw()noexcept; void operator delete(void* ptr, const std::nothrow_t&)throw()noexcept; void* operator new[](std::size_t size) throw(std::bad_alloc); void* operator new[](std::size_t size, const std::nothrow_t&)throw()noexcept; void operator delete[](void* ptr)throw()noexcept; void operator delete[](void* ptr, const std::nothrow_t&)throw()noexcept; void* operator new (std::size_t size, void* ptr)throw()noexcept; void* operator new[](std::size_t size, void* ptr)throw()noexcept; void operator delete (void* ptr, void*)throw()noexcept; void operator delete[](void* ptr, void*)throw()noexcept;
After p.4
void* operator new(std::size_t size, const std::nothrow_t&)throw()noexcept;
After p.9
void operator delete(void* ptr)throw()noexcept;
After p.15
void operator delete(void* ptr, const std::nothrow_t&)throw()noexcept;
After p.4
void* operator new[](std::size_t size, const std::nothrow_t&)throw()noexcept;
After p.8
void operator delete[](void* ptr)throw()noexcept;
After p.13
void operator delete[](void* ptr, const std::nothrow_t&)After p.1
void* operator new(std::size_t size, void* ptr)throw()noexcept;
After p.4
void* operator new[](std::size_t size, void* ptr)throw()noexcept;
After p.6
void operator delete(void* ptr, void*)throw()noexcept;
After p.9
void operator delete[](void* ptr, void*)throw()noexcept;
namespace std { class bad_alloc : public exception { public: bad_alloc()throw()noexcept; bad_alloc(const bad_alloc&)throw()noexcept; bad_alloc& operator=(const bad_alloc&)throw()noexcept; virtual const char* what() constthrow()noexcept; }; }
After p.1
bad_alloc()throw()noexcept;
After p.3
bad_alloc(const bad_alloc&)throw()noexcept; bad_alloc& operator=(const bad_alloc&)throw()noexcept;
After p.4
virtual const char* what() constthrow()noexcept;
namespace std { class bad_array_new_length : public bad_alloc { public: bad_array_new_length()throw()noexcept; }; }
After p.1
bad_array_new_length()throw()noexcept;
Before p.1
new_handler set_new_handler(new_handler new_p)throw()noexcept;
namespace std { class type_info { public: virtual ~type_info(); bool operator==(const type_info& rhs) const; bool operator!=(const type_info& rhs) const; bool before(const type_info& rhs) const; size_t hash_code() constthrow()noexcept; const char* name() const; type_info(const type_info& rhs) = delete; / cannot be copied type_info& operator=(const type_info& rhs) = delete; // cannot be copied }; }
After p.6
size_t hash_code() constthrow()noexcept;
namespace std { class bad_cast : public exception { public: bad_cast()throw()noexcept; bad_cast(const bad_cast&)throw()noexcept; bad_cast& operator=(const bad_cast&)throw()noexcept; virtual const char* what() constthrow()noexcept; }; }
After p.1
bad_cast()throw()noexcept;
After p.3
bad_cast(const bad_cast&)throw()noexcept; bad_cast& operator=(const bad_cast&)throw()noexcept;
After p.4
virtual const char* what() constthrow()noexcept;
namespace std { class bad_typeid : public exception { public: bad_typeid()throw()noexcept; bad_typeid(const bad_typeid&)throw()noexcept; bad_typeid& operator=(const bad_typeid&)throw()noexcept; virtual const char* what() constthrow()noexcept; }; }
After p.1
bad_typeid()throw()noexcept;
After p.3
bad_typeid(const bad_typeid&)throw()noexcept; bad_typeid& operator=(const bad_typeid&)throw()noexcept;
After p.4
virtual const char* what() constthrow()noexcept;
1 The header
namespace std { class exception; class bad_exception; class nested_exception; typedef void (*unexpected_handler)(); unexpected_handler set_unexpected(unexpected_handler f)throw()noexcept; [[noreturn]] void unexpected(); typedef void (*terminate_handler)(); terminate_handler set_terminate(terminate_handler f)throw()noexcept; [[noreturn]] void terminate(); bool uncaught_exception()throw()noexcept; typedef unspecified exception_ptr; exception_ptr current_exception(); [[noreturn]] void rethrow_exception(exception_ptr p); template<class E> exception_ptr make_exception_ptr(E e); [[noreturn]] template <class T> void throw_with_nested(T&& t); template <class E> void rethrow_if_nested(const E& e); }
namespace std { class exception { public: exception()throw()noexcept; exception(const exception&)throw()noexcept; exception& operator=(const exception&)throw()noexcept; virtual ~exception()throw()noexcept; virtual const char* what() constthrow()noexcept; }; }
After p.2
exception()throw()noexcept;
After p.4
exception(const exception& rhs)throw()noexcept; exception& operator=(const exception& rhs)throw()noexcept;
After p.6
virtual ~exception()throw()noexcept;
After p.8
virtual const char* what() constthrow()noexcept;
namespace std { class bad_exception : public exception { public: bad_exception()throw()noexcept; bad_exception(const bad_exception&)throw()noexcept; bad_exception& operator=(const bad_exception&)throw()noexcept; virtual const char* what() constthrow()noexcept; }; }
After p.1
bad_exception()throw()noexcept;
After p.3
bad_exception(const bad_exception&)throw()noexcept; bad_exception& operator=(const bad_exception&)throw()noexcept;
After p.4
virtual const char* what() constthrow()noexcept;
Before p.1
terminate_handler set_terminate(terminate_handler f)throw()noexcept;
Before p.1
bool uncaught_exception()throw()noexcept;
namespace std { class nested_exception { public: nested_exception()throw()noexcept; nested_exception(const nested_exception&)throw()= default; nested_exception& operator=(const nested_exception&)throw()= default; virtual ~nested_exception() = default; // access functions [[noreturn]] void rethrow_nested() const; exception_ptr nested_ptr() const; }; [[noreturn]] template <class T> void throw_with_nested(T&& t); template <class E> void rethrow_if_nested(const E& e); }
After p.2
nested_exception()throw()noexcept;
After p.2
namespace std { class system_error : public runtime_error { public: system_error(error_code ec, const string& what_arg); system_error(error_code ec, const char* what_arg); system_error(error_code ec); system_error(int ev, const error_category& ecat, const string& what_arg); system_error(int ev, const error_category& ecat, const char* what_arg); system_error(int ev, const error_category& ecat); const error_code& code() constthrow()noexcept; const char* what() constthrow()noexcept; }; } // namespace std
After p.12
const error_code& code() constthrow()noexcept;
After p.13
const char *what() constthrow()noexcept;
namespace std { // 20.9.1, allocator argument tag struct allocator_arg_t { }; constexpr allocator_arg_t allocator_arg = allocator_arg_t(); // 20.9.2, uses_allocator template <class T, class Alloc> struct uses_allocator; // 20.9.3, pointer traits template <class Ptr> struct pointer_traits; template <class T> struct pointer_traits<T*>; // 20.9.4, allocator traits template <class Alloc> struct allocator_traits; // 20.9.5, the default allocator: template <class T> class allocator; template <> class allocator<void>; template <class T, class U> bool operator==(const allocator<T>&, const allocator<U>&)throw()noexcept; template lt;class T, class U> bool operator!=(const allocator<T>&, const allocator<U>&)throw()noexcept; ... Rest of synopsis }
namespace std { template <class T> class allocator; // specialize for void: template <> class allocator<void> { public: typedef void* pointer; typedef const void* const_pointer; // reference-to-void members are impossible. typedef void value_type; template <class U> struct rebind { typedef allocator<U> other; }; }; template <class T> class allocator { public: typedef size_t size_type; typedef ptrdiff_t difference_type; typedef T* pointer; typedef const T* const_pointer; typedef T& reference; typedef const T& const_reference; typedef T value_type; template <class U> struct rebind { typedef allocator<U> other; }; allocator()throw()noexcept; allocator(const allocator&)throw()noexcept; template <class U> allocator(const allocator<U>&)throw()noexcept; ~allocator()throw()noexcept; pointer address(reference x) const; const_pointer address(const_reference x) const; pointer allocate(size_type, allocator<void>::const_pointer hint = 0); void deallocate(pointer p, size_type n); size_type max_size() constthrow()noexcept; template<class U, class... Args> void construct(U* p, Args&&... args); template <class U> void destroy(U* p); }; }
After p.10
size_type max_size() constthrow()noexcept;
Before p.1
template <class T1, class T2> bool operator==(const allocator<T1>&, const allocator<T2>&)throw()noexcept;
After p.1
template <class T1, class T2> bool operator!=(const allocator<T1>&, const allocator<T2>&)throw()noexcept;
namespace std { // 22.3.1, locale: class locale; template <class Facet> const Facet& use_facet(const locale&); template <class Facet> bool has_facet(const locale&)throw()noexcept; ... Rest of synopsis }
namespace std { class locale { public: // types: class facet; class id; typedef int category; static const category // values assigned here are for exposition only none = 0, collate = 0x010, ctype = 0x020, monetary = 0x040, numeric = 0x080, time = 0x100, messages = 0x200, all = collate | ctype | monetary | numeric | time | messages; // construct/copy/destroy: locale()throw()noexcept; locale(const locale& other)throw()noexcept; explicit locale(const char* std_name); explicit locale(const string& std_name); locale(const locale& other, const char* std_name, category); locale(const locale& other, const string& std_name, category); template <class Facet> locale(const locale& other, Facet* f); locale(const locale& other, const locale& one, category); ~locale()throw()noexcept; // not virtual const locale& operator=(const locale& other)throw()noexcept; template <class Facet> locale combine(const locale& other) const; // locale operations: basic_string<char> name() const; bool operator==(const locale& other) const; bool operator!=(const locale& other) const; template <class charT, class Traits, class Allocator> bool operator()(const basic_string<charT,Traits,Allocator>& s1, const basic_string<charT,Traits,Allocator>& s2) const; // global locale objects: static locale global(const locale&); static const locale& classic(); }; }
Before p.1
locale()throw()noexcept;
After p.2
locale(const locale& other)throw()noexcept;
After p.3
const locale& operator=(const locale& other)throw()noexcept;
After p.17
~locale()throw();
After p.4
templatebool has_facet(const locale& loc) throw()noexcept;
namespace std { template <> class ctype<char> : public locale::facet, public ctype_base { public: typedef char char_type; ... const mask* table() constthrow()noexcept; static const mask* classic_table()throw()noexcept; ... }; }
After p.11
const mask* table() constthrow()noexcept;
Before p.1
static const mask* classic_table()throw()noexcept;
namespace std { class codecvt_base { public: enum result { ok, partial, error, noconv }; }; template <class internT, class externT, class stateT> class codecvt : public locale::facet, public codecvt_base { public: typedef internT intern_type; typedef externT extern_type; typedef stateT state_type; explicit codecvt(size_t refs = 0); result out(stateT& state, const internT* from, const internT* from_end, const internT*& from_next, externT* to, externT* to_end, externT*& to_next) const; result unshift(stateT& state, externT* to, externT* to_end, externT*& to_next) const; result in(stateT& state, const externT* from, const externT* from_end, const externT*& from_next, internT* to, internT* to_end, internT*& to_next) const; int encoding() constthrow()noexcept; bool always_noconv() constthrow()noexcept; int length(stateT&, const externT* from, const externT* end, size_t max) const; int max_length() constthrow()noexcept; static locale::id id; protected: ~codecvt(); virtual result do_out(stateT& state, const internT* from, const internT* from_end, const internT*& from_next, externT* to, externT* to_end, externT*& to_next) const; virtual result do_in(stateT& state, const externT* from, const externT* from_end, const externT*& from_next, internT* to, internT* to_end, internT*& to_next) const; virtual result do_unshift(stateT& state, externT* to, externT* to_end, externT*& to_next) const; virtual int do_encoding() constthrow()noexcept; virtual bool do_always_noconv() constthrow()noexcept; virtual int do_length(stateT&, const externT* from, const externT* end, size_t max) const; virtual int do_max_length() constthrow()noexcept; }; }
After p.3
int encoding() constthrow()noexcept;
After p.4
bool always_noconv() constthrow()noexcept;
After p.6
int max_length() constthrow()noexcept;
After p.8
int do_encoding() constthrow()noexcept;
After p.9
bool do_always_noconv() constthrow()noexcept;
After p.13
int do_max_length() constthrow()noexcept;
2. The result of operator*() on an end-of-stream iterator is undefined. For any other iterator value a char_- type value is returned. It is impossible to assign a character via an input iterator.
namespace std { template<class charT, class traits = char_traits<charT> > class istreambuf_iterator : public iterator<input_iterator_tag, charT, typename traits::off_type, unspecified , charT> { public: typedef charT char_type; typedef traits traits_type; typedef typename traits::int_type int_type; typedef basic_streambuf<charT,traits> streambuf_type; typedef basic_istream<charT,traits> istream_type; class proxy; // exposition only constexpr istreambuf_iterator()throw()noexcept; istreambuf_iterator(const istreambuf_iterator&)throw()= default; ~istreambuf_iterator()throw()= default; istreambuf_iterator(istream_type& s)throw()noexcept; istreambuf_iterator(streambuf_type* s)throw()noexcept; istreambuf_iterator(const proxy& p)throw()noexcept; charT operator*() const; pointer operator->() const; istreambuf_iterator<charT,traits>& operator++(); proxy operator++(int); bool equal(const istreambuf_iterator& b) const; private: streambuf_type* sbuf_; // exposition only }; template <class charT, class traits> bool operator==(const istreambuf_iterator<charT,traits>& a, const istreambuf_iterator<charT,traits>& b); template <class charT, class traits> bool operator!=(const istreambuf_iterator<charT,traits>& a, const istreambuf_iterator<charT,traits>& b); }
Before p.1
constexpr istreambuf_iterator()throw()noexcept;
After p.1
istreambuf_iterator(basic_istream& s) throw()noexcept; istreambuf_iterator(basic_streambuf* s) throw()noexcept;
After p.2
istreambuf_iterator(const proxy& p)throw()noexcept;
namespace std { template <class charT, class traits = char_traits<charT> > class ostreambuf_iterator : public iterator<output_iterator_tag, void, void, void, void> { public: typedef charT char_type; typedef traits traits_type; typedef basic_streambuf<charT,traits> streambuf_type; typedef basic_ostream<charT,traits> ostream_type; public: ostreambuf_iterator(ostream_type& s)throw()noexcept; ostreambuf_iterator(streambuf_type* s)throw()noexcept; ostreambuf_iterator& operator=(charT c); ostreambuf_iterator& operator*(); ostreambuf_iterator& operator++(); ostreambuf_iterator& operator++(int); bool failed() constthrow()noexcept; private: streambuf_type* sbuf_; // exposition only }; }
Before p.1
ostreambuf_iterator(ostream_type& s)throw()noexcept;
After p.2
ostreambuf_iterator(streambuf_type* s)throw()noexcept;
After p.4
bool failed() constthrow()noexcept;
namespace std { class future_error : public logic_error { public: future_error(error_code ec); // exposition only const error_code& code() constthrow()noexcept; const char* what() constthrow()noexcept; }; }
Before p.1
const error_code& code() constthrow()noexcept;
After p.1
const char *what() constthrow()noexcept;