Doc. no. | P0003R0 |
Date: | 2015-09-28 |
Project: | Programming Language C++ |
Reply to: | Alisdair Meredith <ameredith1@bloomberg.net> |
Dynamic exception specifications were deprecated in C++11. This paper formally proposes removing the feature from C++17, while retaining the (still) deprecated throw() specification strictly as an alias for noexcept(true).
Exception specifications were added as part of the original design of the exception language feature. However, the experience of using them was less than desired, with the general consensus by the time the 1998 standard was published being, generally, to not use them. The standard library made the explicit choice to not use exception specifications apart from a handful of places where guaranteeing an empty (no-throwing) specification seemed useful. N741 gives a brief summary of the LWG thoughts at the time.
By the time C++11 was published, the feeling against exception specifications had grown, the limited real-world use generally reported negative user experience, and the language feature, renamed as dynamic exception specifcations, was deprecated, (N3051). A new languge feature, noexcept, was introduced to describe the important case of knowing when a function could guarantee to not throw any exceptions.
Looking ahead to C++17, there is a desire to incorporate exception specifications into the type system, N4533. This solves a number of awkward corners that arise from exception specifications not being part of the type of a function, but still does nothing for the case of deprecated dynamic exception specifications, so the actual language does not get simpler, and we must still document and teach the awkwards corners that arise from dynamic exception specifications outside the type system.
The recommendation of this paper is to remove dynamic exception specifications from the language. However, the syntax of the throw() specification should be retained, but no longer as a dyanmic exception specification. Its meaning should become strictly an alias for noexcept(true), and its usage should remain deprecated.
To minimize the impact on the current standard wording, the grammar term exception-specification is retained, although it has only one production and could be replaced entirely with noexcept-specification. Alternatively, the grammar term noexcept-specification could be retired.
The wording changes in this initial proposal are deliberately minimal in an effort to ensure the least risk of accidental change. However, rather than using the language of sets of compatible exception specifications (where there are now only two such sets, the empty set and the set of all types) it would be possible to write a simpler, clearer form describing instead functions that permit exceptions, and functions that permit no exceptions. While such a specification would be preferred, it is also beyond the drafting skills of the proposal author.
The redrafting goes slightly beyond minimal by eliminating use of the pseudo-type "any". This change, while improving clarity, also avoids confusion with the standard library type any from the Library Fundamentals TS in the event that it becomes part of a future standard library.
Dynamic exception specifications are a failed experiment, but this is not immediately clear to novices, especially where the "novice" is an experienced developer coming from other languages such as Java, where exception specifications may be more widely used. Their continuing presence, especially in an important part of the languge model (exceptions are key to understanding constructors, destructors, and RAII) is an impediment that must be explained. It remains embarassing to explain to new developers that there are features of the language that conforming compilers are required to support, yet should never be used.
Exception specifications in general occupy an awkward corner of the grammer, where they do not affect the type system, yet critically affect how a virtual function can be overridden, or which functions can bind to certain function pointer variables. As noted above, N4533 would go a long way to resolving that problem for noexcept exception specifications, which makes the hole left for dynamic exception specifications even more awkward and unusual for the next generation of C++ developers.
C++17 is on schedule to be a release with several breaking changes for old code, with the standard library removing auto_ptr, the classic C++98 fuction binders, and more. Similarly, it is expected that the core language will lose trigraphs, the register keyword, and the increment operator for bool. This would be a good time to make a clean break with long discouraged (and actively deprecated) parts of the language.
The proposed change would resolve core issue 596 as no longer relevant (NAD), and should simplify core issue 1351, although that is marked as a Defect Report that was not yet applied to the working paper the proposed wording below was drafted from.
There is certainly some body of existing code that has not heeded the existing best practice of discouraging dynamic exception specifications, and has not yet accounted for the feature being deprecated in C++11. It is not clear how much of such code would be expected to port unmodified into a C++17 (or beyond) world, and the change is relatively simple - just strike the (non-empty) dynamic exception specification to retain equivalent meaning. The key difference is that the unexpected handler will not now be called to translate unexpected exceptions. In rare cases, this would allow a new exception type to propage, rather than calling terminate to abort. If enforcing that semantic is seen as important in production systems, there is a more intrusive workaround available:
void legacy()throw(something)try { // function body as before } catch(const something&) { throw; } catch(...) { terminate(); }
It is thought that the empty dynamic exception specification, throw(), was much more widely used in practice, often in the mistaken impression that compilers would use this information to optimize code generation where no exception could propagate, where in fact this is strictly a pessimization that forces stack unwinding to the function exit, and then calling the unexcepted callback in a manner that is guatanteed to fail before the subsequent call to terminate. This paper proposes treating such (still deprecate) exception specifications as synonyms for noexcept(true), yielding the performance benefit many were originally expecting.
It should also be noted that at least one widely distributed compiler has still not implemented this feature in 2015, and at least one vendor has expressed a desire to never implement the deprecated feature (while that vendor has implemented the noexcept form of exception specification). Code on that platform would not be adversely impacted by the proposed removal, and portable code must always have allowed for the idiosynracies of this platform.
One remaining task is to survey popular open source libraries and see what level of usage, if any, remains in large, easily accessible codebases.
1 The following rules describe the scope of names declared in classes.
1) The potential scope of a name declared in a class consists not only of the declarative region following the name’s point of declaration, but also of all function bodies, default arguments,
7 A name used in the definition of a class X outside of a member function body,
default argument, exception-specification, brace-or-equal-initializer
of a non-static data member, or nested class definition29 shall be declared in one
of the following ways:
8 For the members of a class X, a name used in a member function body, in a default
argument, in an exception-specification, in the brace-or-equal-initializer
of a non-static data member (9.2), or in the definition of a class member outside of the definition
of X, following the member's declarator-id31, shall be declared in one
of the following ways:
31) That is, an unqualified name that occurs, for instance, in a type in the
parameter-declaration-clause or in the exception-specification.
4 [ Example:
struct S { constexpr S() = default; // ill-formed: implicit S() is not constexpr S(int a = 0) = default; // ill-formed: default argument void operator=(const S&) = default; // ill-formed: non-matching return type ~S() noexcept(false)throw(int)= default; // deleted: exception specification does not match private: int i; S(S&); // OK: private copy constructor }; S::S(S&) = default; // OK: defines copy constructor
— end example ]
2 A class is considered a completely-defined object type (3.9) (or complete type) at the closing
} of the class-specifier. Within the class member-specification, the class
is regarded as complete within function bodies, default arguments, using-declarations
introducing inheriting constructors (12.9), exception-specifications, and
brace-or-equal-initializers for non-static data members (including such things in nested
classes). Otherwise it is regarded as incomplete within its own class member-specification.
3 [ Note: A single name can denote several function members provided their types are sufficiently different (Clause13). — end note ]
4 A brace-or-equal-initializer shall appear only in the declaration of a data member.
(For static data members, see 9.4.2; for non-static data members, see 12.6.2). A
brace-or-equal-initializer for a non-static data member shall not directly or indirectly
cause the implicit definition of a defaulted default constructor for the enclosing class or
the exception specification of that constructor.
2 For purposes of name lookup and instantiation, default arguments and
exception-specifications of function templates and default arguments and
exception-specifications of member functions of class templates are considered
definitions; each default argument or exception-specification is a separate
definition which is unrelated to the function template definition or to any other default
arguments or exception-specifications.
(4.7) — In a dynamic-exception-specification (15.4); the pattern is a type-id.
11 [Note: For purposes of name lookup, default arguments and
exception-specifications of function templates and default arguments and
exception-specifications of member functions of class templates are considered
definitions(14.5). — end note ]
3 For an exception-specification of a function template specialization or specialization of
a member function of a class template, if the exception-specification is implicitly
instantiated because it is needed by another template specialization and the context that requires
it depends on a template parameter, the point of instantiation of the exception-specification
is the point of instantiation of the specialization that requires it. Otherwise, the point of
instantiation for such an exception-specification immediately follows the namespace scope
declaration or definition that requires the exception-specification.
1 Unless a class template specialization has been explicitly instantiated (14.7.2) or explicitly
specialized (14.7.3), the class template specialization is implicitly instantiated when the
specialization is referenced in a context that requires a completely-defined object type or when
the completeness of the class type affects the semantics of the program. [ Note: Within a
template declaration, a local class or enumeration and the members of a local class are never
considered to be entities that can be separately instantiated (this includes their default arguments,
exception-specifications, and non-static data member initializers, if any). As a
result, the dependent names are looked up, the semantic constraints are checked, and any templates
used are instantiated as part of the instantiation of the entity within which the local class or
enumeration is declared. — end note ] The implicit instantiation of a class template
specialization causes the implicit instantiation of the declarations, but not of the definitions,
or default arguments, or exception-specifications of the class
member functions, member classes, scoped member enumerations, static data members and member
templates; and it causes the implicit instantiation of the definitions of unscoped member
enumerations and member anonymous unions. However, for the purpose of determining whether an
instantiated redeclaration of a member is valid according to 9.2, a declaration that corresponds to
a definition in the template is considered to be a definition. [ Example:
15 The exception-specification of a function template specialization is not instantiated
along with the function declaration; it is instantiated when needed (15.4). If such an
exception-specification is needed but has not yet been instantiated, the dependent names are
looked up, the semantics constraints are checked, and the instantiation of any template used in the
exception-specification is done as if it were being done as part of instantiating the
declaration of the specialization at that point.
12 The usual access checking rules do not apply to names used to specify explicit instantiations.
[ Note: In particular, the template arguments and names used in the function declarator
(including parameter types, and return types and exception
specifications) may be private types or objects which would normally not be accessible and
the template may be a member template or member function which would not normally be accessible.
— end note ]
7 The substitution occurs in all types and expressions that are used in the function type and in
template parameter declarations. The expressions include not only constant expressions such as those
that appear in array bounds or as nontype template arguments but also general expressions (i.e.,
non-constant expressions) inside sizeof, decltype, and other contexts that allow
non-constant expressions. The substitution proceeds in lexical order and stops when a condition that
causes deduction to fail is encountered. [ Note: The equivalent substitution in
exception specifications is done only when the exception-specification is instantiated, at
which point a program is ill-formed if the substitution results in an invalid type or expression.
— end note ]
1 The exception specification of a function is a (possibly empty) set
of types, indicating that the function might exit via an exception that matches a
handler of one of the types in the set; the (conceptual) set of all types is used
to denote that the function might exit via an exception of arbitrary type. If the
set is empty, the function is said to have a non-throwing exception specification.
The exception specification is either defined explicitly by using an
exception-specification as a suffix of a function declaration's declarator (8.3.5)
or implicitly.
exception-specification:dynamic-exception-specificationnoexcept-specificationnoexcept ( constant-expression ) noexcept throw ( )dynamic-exception-specification:throw ( type-id-listopt )type-id-list:type-id ...opttype-id-list , type-id ...optnoexcept-specification:noexcept ( constant-expression )noexcept
In an exceptionnoexcept-specification, the
constant-expression, if supplied, shall be a constant expression (5.20) that is
contextually converted to bool (Clause 4). A ( token that follows
noexcept is part of the exceptionnoexcept-specification
and does not commence an initializer (8.5). The exception-specification
throw() is deprecated (see Annex D), and equivalent to the exception-specification
noexcept.
2 An exception-specification shall appear only on a function declarator for a function type, pointer to function type, reference to function type, or pointer to member function type that is the top-level type of a declaration or definition, or on such a type appearing as a parameter or return type in a function declarator. An exception-specification shall not appear in a typedef declaration or alias-declaration. [Example:
void f() noexcept— end example ]throw(int); // OK void (*fp)() noexceptthrow (int); // OK void g(void pfa() noexceptthrow(int)); // OK typedef int (*pf)() noexceptthrow(int); // ill-formed
A type denoted in a dynamic-exception-specification shall not denote an incomplete
type or an rvalue reference type. A type denoted in a dynamic-exception-specification
shall not denote a pointer or reference to an incomplete type, other than "pointer to cv
void". A type cv T, "array of T", or
"function returning T" denoted in a dynamic-exception-specification
is adjusted to type T, "pointer to T", or "pointer to function
returning T", respectively. A dynamic-exception-specification denotes an
exception specification that is the set of adjusted types specified thereby.
3 The exception-specification noexcept or noexcept(constant-expression), where the constant-expression yields true, denotes an exception specification that is the empty set. The exception-specification noexcept(constant-expression), where the constant-expression yields false, or the absence of an exception-specification in a function declarator other than that for a destructor (12.4) or a deallocation function (3.7.4.2) denotes an exception specification that is the set of all types.
4 Two exception-specifications are compatible if the sets of types they denote are the same.
5 If any declaration of a function has an exception-specification that is not a
noexcept-specification allowing noall exceptions, all declarations,
including the definition and any explicit specialization, of that function shall have a compatible
exception-specification. If any declaration of a pointer to function, reference to
function, or pointer to member function has an exception-specification, all occurrences of that
declaration shall have a compatible exception-specification. If a declaration of a function
has an implicit exception specification, other declarations of the function shall not specify an
exception-specification. In an explicit instantiation an exception-specification may
be specified, but is not required. If an exception-specification is specified in an explicit
instantiation directive, it shall be compatible with the exception-specifications of other
declarations of that function. A diagnostic is required only if the exception-specifications
are not compatible within a single translation unit.
6 If a virtual function has an exception specification, all declarations, including the definition, of any function that overrides that virtual function in any derived class shall only allow exceptions that are allowed by the exception specification of the base class virtual function, unless the overriding function is defined as deleted. [ Example:
struct B { virtual void f() noexceptThe declaration of D::f is ill-formed because it allows all exceptions, whereas B::f allows no exceptionsthrow (int, double); virtual void g(); }; struct D: B { void f(); // ill-formed void g() noexeptthrow (int); // OK };
class A { /∗...∗/ }; void (*pf1)(); // no exception specification void (*pf2)() noexcept— end example ]throw(A); void f() { pf1 = pf2; // OK: pf1 is less restrictive pf2 = pf1; // error: pf2 is more restrictive }
7 In such an assignment or initialization, exception-specifications on return types and parameter types shall be compatible. In other assignments or initializations, exception-specifications shall be compatible.
8 An exception-specification can include the same type more than once and can include
classes that are related by inheritance, even though doing so is redundant. [ Note: An
exception-specification can also include the class std::bad_exception (18.8.2).
— end note ]
9 A function is said to allow an exception of type E if its exception specification
contains a type T for which a handler of type T would be a match (15.3) for an
exception of type E. A function is said to allow all exceptions if its exception
specification is the set of all types. Otherwise a function does not allow any exceptions.
10 Whenever an exception of type E is thrown and the search for a handler (15.3)
encounters the outermost block of a function with an exception specification that does not allow
any exceptionsE, then, the function
std::terminate() is called (15.5.1).
(10.1) — if the function definition has a dynamic-exception-specification, the function
std::unexpected() is called (15.5.2),
(10.2) — otherwise, the function std::terminate() is called (15.5.1).
[ Example:
class X { };class Y { };class Z: public X { };class W { };void f() throw (X, Y) {int n = 0;if (n) throw X(); // OKif (n) throw Z(); // also OKthrow W(); // will call std::unexpected()}
— end example ]
[Note: A function can have multiple declarations with different non-throwing
exception-specifications; for this purpose, the one on the function definition is used.
— end note ]
11 An implementation shall not reject an expression merely because when executed it throws or might throw an exception that the containing function does not allow. [ Example:
extern void f() throw(the call to f is well-formed even though when called, f might throw an exceptionX, Y); void g()throw(X){ f(); // OK }
12 [ Note: An exception specification is not considered part of a function’s type; see 8.3.5. — end note ]
13 A potential exception of a given context is either a type that might be thrown as an exception or a pseudo-type, denoted by "any", that represents the situation where an exception of an arbitrary type might be thrown. A subexpression e1 of an expression e is an immediate subexpression if there is no subexpression e2 of e such that e1 is a subexpression of e2.
14 The set of potential exceptions of a function, function pointer, or member function pointer
f is the set denoted by the exception specification of f.defined as
follows:
(14.1) — If the exception specification of f is the set of all types, the set consists
of the pseudo-type "any".
(14.2) — Otherwise, the set consists of every type in the exception specification of f.
15 The set of potential exceptions of an expression e is empty if e is a core constant expression (5.20). Otherwise, it is the union of the sets of potential exceptions of the immediate subexpressions of e, including default argument expressions used in a function call, combined with a set S defined by the form of e, as follows:
(15.1) — If e is a function call (5.2.2):
(15.1.1) — If its postfix-expression is a (possibly parenthesized) id-expression (5.1.1), class member access (5.2.5), or pointer-to-member operation (5.5) whose cast-expression is an id-expression, S is the set of potential exceptions of the entity selected by the contained id-expression (after overload resolution, if applicable).
(15.1.2) — Otherwise, S is the set of all typescontains the pseudo-type
"any".
(15.2) — If e implicitly invokes a function (such as an overloaded operator, an allocation function in a new-expression, or a destructor if e is a full-expression (1.9)), S is the set of potential exceptions of the function.
(15.3) — if e is a throw-expression (5.17), S is the set of all
typesconsists of the type of the exception object that would be initialized by the operand,
if present, or the pseudo-type "any" otherwise.
(15.4) — if e is a dynamic_cast expression that casts to a reference type and
requires a run-time check (5.2.7), S is the set of all typesconsists of the
type std::bad_cast.
(15.5) — if e is a typeid expression applied to a glvalue expression whose type is a
polymorphic class type (5.2.8), S is the set of all typesconsists of the type
std::bad_typeid.
(15.6) — if e is a new-expression with a non-constant expression in the
noptr-new-declarator (5.3.4), S is the set of all typesconsists of the
type std::bad_array_new_length.
[ Example: Given the following declarations
void f()the set of potential exceptions for some sample expressions is:throw(int)noexcept(false); void g(); struct A { A(); }; struct B { B() noexcept; }; struct D { D()throw (double)noexcept(false); };
(15.76.1) — for f(), the set is the set of all types
consists of int;
(15.86.2) — for g(), the set is the set of all types
consists of "any";
(15.96.3) — for new A, the set is the set of all types
consists of "any";
(15.106.4) — for B(), the set is empty;
(15.116.5) — for new D, the set is the set of all types
consists of "any" and double.
— end example ]
16 Given a member function f of some class X, where f is an inheriting constructor (12.9) or an implicitly-declared special member function, the set of potential exceptions of the implicitly-declared member function f consists of all the members from the following sets:
(16.1) — if f is a constructor,
(16.1.1) — the sets of potential exceptions of the constructor invocations
(16.1.1.1) — for X's non-variant non-static data members,
(16.1.1.2) — for X's direct base classes, and
(16.1.1.3) — if X is non-abstract (10.4), for X's virtual base classes,
(including default argument expressions used in such invocations) as selected by overload resolution for the implicit definition of f (12.1). [ Note: Even though destructors for fully-constructed subobjects are invoked when an exception is thrown during the execution of a constructor (15.2), their exception specifications do not contribute to the exception specification of the constructor, because an exception thrown from such a destructor could never escape the constructor (15.1, 15.5.1). — end note]
(16.1.2) — the sets of potential exceptions of the initialization of non-static data members from brace-or-equal-initializers that are not ignored (12.6.2);
(16.2) — if f is an assignment operator, the sets of potential exceptions of the assignment operator invocations for X's non-variant non-static data members and for X's direct base classes (including default argument expressions used in such invocations), as selected by overload resolution for the implicit definition of f (12.8);
(16.3) — if f is a destructor, the sets of potential exceptions of the destructor invocations for X's non-variant non-static data members and for X's virtual and direct base classes.
17 An inheriting constructor (12.9) and an implicitly-declared special member function (Clause 12) are considered to have an implicit exception specification, as follows, where S is the set of potential exceptions of the implicitly-declared member function:
(17.1) — if S is the set of all typescontains the pseudo-type
"any", the implicit exception specification is the set of all types;
(17.2) — otherwise, the implicit exception specification contains all the types in S.
[ Note: An instantiation of an inheriting constructor template has an implied exception specification as if it were a non-template inheriting constructor. — end note ] [ Example:
struct A { A(int = (A(5), 0)) noexcept; A(const A&) throw(); A(A&&) throw(); ~A()throw(X); }; struct B { B() throw(); B(const B&) = default; // exception specification contains no types B(B&&, int = (throw Y(), 0)) noexcept; ~B() noexcept(false)throw(Y); }; int n = 7; struct D : public A, public B { int * p = new (std::nothrow) int[n]; // exception specification of D::D() contains the set of all typesX and std::bad_array_new_length// exception specification of D::D(const D&) contains no types // exception specification of D::D(D&&) contains the set of all typesY// exception specification of D::~D() contains the set of all typesX and Y};
Furthermore, if A::~A() or B::~B() were virtual, D::~D() would
not be as restrictive as that of A::~A, and the program would be ill-formed since a function
that overrides a virtual function from a base class shall have an exception-specification at
least as restrictive as that in the base class. — end example ]
18 A deallocation function (3.7.4.2) with no explicit exception-specification has an exception specification that is the empty set.
19 An exception-specification is considered to be needed when:
(19.1) — in an expression, the function is the unique lookup result or the selected member of a set of overloaded functions (3.4, 13.3, 13.4);
(19.2) — the function is odr-used (3.2) or, if it appears in an unevaluated operand, would be odr-used if the expression were potentially-evaluated;
(19.3) — the exception-specification is compared to that of another declaration (e.g., an explicit specialization or an overriding virtual function);
(19.4) — the function is defined; or
(19.5) — the exception-specification is needed for a defaulted special member function that calls the function. [ Note: A defaulted declaration does not require the exception-specification of a base member function to be evaluated until the implicit exception-specification of the derived function is needed, but an explicit exception-specification needs the implicit exception-specification to compare against. — end note ]
The exception-specification of a defaulted special member function is evaluated as described above only when needed; similarly, the exception-specification of a specialization of a function template or member function of a class template is instantiated only when needed.
20 In a dynamic-exception-specification, a type-id followed by an ellipsis is a
pack expansion (14.5.3).
21 [ Note: The use of dynamic-exception-specifications is deprecated (see Annex D).
— end note ]
1 The functions std::terminate() (15.5.1) and std::unexpected()
(15.5.2) areis used by the exception handling mechanism for coping with errors
related to the exception handling mechanism itself. The function std::current_exception()
(18.8.5) and the class std::nested_exception (18.8.6) can be used by a program to capture
the currently handled exception.
1 In some situations exception handling must be abandoned for less subtle error handling techniques. [ Note: These situations are:
(1.1) — when the exception handling mechanism, after completing the initialization of the exception object but before activation of a handler for the exception (15.1), calls a function that exits via an exception, or
(1.2) — when the exception handling mechanism cannot find a handler for a thrown exception (15.3), or
(1.3) — when the search for a handler (15.3) encounters the outermost block of a function with
an exceptionnoexcept-specification that does not allow the
exception (15.4), or
(1.4) — when the destruction of an object during stack unwinding (15.2) terminates by throwing an exception, or
(1.5) — when initialization of a non-local variable with static or thread storage duration (3.6.2) exits via an exception, or
(1.6) — when destruction of an object with static or thread storage duration exits via an exception (3.6.3), or
(1.7) — when execution of a function registered with std::atexit or std::at_quick_exit exits via an exception (18.5), or
(1.8) — when a throw-expression (5.17) with no operand attempts to rethrow an exception and no exception is being handled (15.1), or
(1.9) — when std::unexpected exits via an exception of a type that is not allowed by
the previously violated exception specification, and std::bad_exception is not included in
that exception specification (15.5.2), or
(1.10) — when the implementation’s default unexpected exception handler is called (D.8.1), or
(1.11) — when the function std::nested_exception::rethrow_nested is called for an object that has captured no exception (18.8.6), or
(1.12) — when execution of the initial function of a thread exits via an exception (30.3.1.2), or
(1.13) — when the destructor or the copy assignment operator is invoked on an object of type std::thread that refers to a joinable thread (30.3.1.3, 30.3.1.4), or
(1.14) — when a call to a wait(), wait_until(), or wait_for() function on a condition variable (30.5.1, 30.5.2) fails to meet a postcondition.
— end note ]
2 In such cases, std::terminate() is called (18.8.3). In the situation where no matching
handler is found, it is implementation-defined whether or not the stack is unwound before
std::terminate() is called. In the situation where the search for a handler (15.3) encounters
the outermost block of a function with an
exceptionnoexcept-specification that does not allow the exception (15.4),
it is implementation-defined whether the stack is unwound, unwound partially, or not unwound at all
before std::terminate() is called. In all other situations, the stack shall not be unwound
before std::terminate() is called. An implementation is not permitted to finish stack
unwinding prematurely based on a determination that the unwind process will eventually cause a call
to std::terminate().
1 If a function with a dynamic-exception-specification exits via an exception of a type that
is not allowed by its exception specification, the function std::unexpected() is called
(D.8) immediately after completing the stack unwinding for the former function.
2 [ Note: By default, std::unexpected() calls std::terminate(), but a
program can install its own handler function (D.8.2). In either case, the constraints in the
following paragraph apply. — end note ]
3 The std::unexpected() function shall not return, but it can throw (or rethrow) an
exception. If it throws a new exception which is allowed by the exception specification which
previously was violated, then the search for another handler will continue at the call of the
function whose exception specification was violated. If it exits via an exception of a type that the
dynamic-exception-specification does not allow, then the following happens: If the
dynamic-exception-specification does not include the class std::bad_exception (18.8.2)
then the function std::terminate() is called, otherwise the thrown exception is replaced by
an implementation-defined object of type std::bad_exception and the search for another
handler will continue at the call of the function whose dynamic-exception-specification was
violated.
4 [Note: Thus, a dynamic-exception-specification guarantees that a function exits only
via an exception of one of the listed types. If the dynamic-exception-specification includes
the type std::bad_exception then any exception type not on the list may be replaced by
std::bad_exception within the function std::unexpected(). — end note]
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. An implementation may strengthen
the exception specification for a non-virtual function by adding a non-throwing
exceptionnoexcept-specification.
2 A function may throw an object of a type not listed in its Throws clause if its type is derived from a type named in the Throws clause and would be caught by an exception handler for the base type.
3 Functions from the C standard library shall not throw exceptions191 except when such a function calls a program-supplied function that throws an exception.192
4 Destructor operations defined in the C++ standard library shall not throw exceptions. Every
destructor in the C++ standard library shall behave as if it had a non-throwing exception
specification. Any other functions efined in the C++ standard library that do not have an
exception-specification may throw implementation-defined exceptions unless otherwise
specified.193 An implementation may strengthen this implicit exception-specification
by adding an explicit one.194
194) That is, an implementation may provide an explicit exception-specification that defines
the subset of "any" exceptions thrown by that function. This implies that the implementation
may list implementation-defined types in such an exception-specification.
1 The header <exception> defines several types and functions related to the handling of exceptions in a C++ program.
namespace std { class exception; class bad_exception; class nested_exception;typedef void (*unexpected_handler)();unexpected_handler get_unexpected() noexcept;unexpected_handler set_unexpected(unexpected_handler f) noexcept;[[noreturn]] void unexpected();typedef void (*terminate_handler)(); terminate_handler get_terminate() noexcept; terminate_handler set_terminate(terminate_handler f) noexcept; [[noreturn]] void terminate() noexcept; int uncaught_exceptions() noexcept; // D.9X, uncaught_exception (deprecated) bool uncaught_exception() noexcept; typedef unspecified exception_ptr; exception_ptr current_exception() noexcept; [[noreturn]] void rethrow_exception(exception_ptr p); template <class E> exception_ptr make_exception_ptr(E e) noexcept; template <class T> [[noreturn]] void throw_with_nested(T&& t); template <class E> void rethrow_if_nested(const E& e); }
1 The class bad_exception defines the type of objects referenced by the
exception_ptr returned from a call to current_exception (18.8.5 [propagation])
when the currently active exception object fails to copythrown as described in
(15.5.2).
4 Member function swap() shall have an
exceptionnoexcept-specification which is equivalent to
noexcept(true).
(2.40) — Throw specifications on a single function declaration [256].
1 The exception-specification throw()use of
dynamic-exception-specifications is deprecated.
typedef void (*unexpected_handler)();
1 The type of a handler function to be called by unexpected() when a function attempts
to throw an exception not listed in its dynamic-exception-specification.
2 Required behavior: An unexpected_handler shall not return. See also 15.5.2.
3 Default behavior: The implementation's default unexpected_handler calls
std::terminate().
unexpected_handler set_unexpected(unexpected_handler f) noexcept;
1 Effects: Establishes the function designated by f as the current
unexpected_handler.
2 Remark: It is unspecified whether a null pointer value designates the default
unexpected_handler.
3 Returns: The previous unexpected_handler.
unexpected_handler get_unexpected() noexcept;
1 Returns: The current unexpected_handler. [ Note: This may be a null pointer
value. — end note ]
[[noreturn]] void unexpected();
1 Remarks: Called by the implementation when a function exits via an exception not allowed by
its exception-specification (15.5.2), in effect after evaluating the throw-expression
(D.8.1). May also be called directly by the program.
2 Effects: Calls the current unexpected_handler function. [ Note: A default
unexpected_handler is always considered a callable handler in this context. —
end note ]