Namespaces
Variants
Views
Actions

Difference between revisions of "cpp/language/except spec"

From cppreference.com
< cpp‎ | language
(Potential exceptions: CWG1992)
(Applied CWG#25 DR and replaced raw HTML lists.)
Line 17: Line 17:
 
This specification may appear only on lambda-declarator or on a function declarator that is the {{rev inl|until=c++17|top-level}} declarator of a function, variable, or non-static data member, whose type is a function type, a pointer to function type, a reference to function type, a pointer to member function type. It may appear on the declarator of a parameter or on the declarator of a return type.
 
This specification may appear only on lambda-declarator or on a function declarator that is the {{rev inl|until=c++17|top-level}} declarator of a function, variable, or non-static data member, whose type is a function type, a pointer to function type, a reference to function type, a pointer to member function type. It may appear on the declarator of a parameter or on the declarator of a return type.
 
{{source|
 
{{source|
void f() throw(int); // OK: function declaration
+
void f() throw(int);           // OK: function declaration
void (*pf)() throw (int); // OK: pointer to function declaration
+
void (*pf)() throw (int);       // OK: pointer to function declaration
void g(void pfa() throw(int)); // OK: pointer to function parameter declaration
+
void g(void pfa() throw(int)); // OK: pointer to function parameter declaration
 
typedef int (*pf)() throw(int); // Error: typedef declaration
 
typedef int (*pf)() throw(int); // Error: typedef declaration
 
}}
 
}}
Line 33: Line 33:
 
===Potential exceptions===
 
===Potential exceptions===
 
Each function {{tt|f}}, pointer to function {{tt|pf}}, and pointer to member function {{tt|pmf}} has a ''set of potential exceptions'', which consists of types that might be thrown. Set of all types indicates that any exception may be thrown. This set is defined as follows:
 
Each function {{tt|f}}, pointer to function {{tt|pf}}, and pointer to member function {{tt|pmf}} has a ''set of potential exceptions'', which consists of types that might be thrown. Set of all types indicates that any exception may be thrown. This set is defined as follows:
<ol>
+
@1@ If the declaration of {{tt|f}}, {{tt|pf}}, or {{tt|pmf}} uses a dynamic exception specification{{rev inl|until=c++11| that does not allow all exceptions}}, the set consists of the types listed in that specification.
<li> If the declaration of {{tt|f}}, {{tt|pf}}, or {{tt|pmf}} uses a dynamic exception specification{{rev inl|until=c++11| that does not allow all exceptions}}, the set consists of the types listed in that specification.
+
 
<li class="t-rev-inl t-rev-inl-noborder t-since-cxx11"><span>{{mark since c++11}} Otherwise, if the declaration of {{tt|f}}, {{tt|pf}}, or {{tt|pmf}} uses {{rlpt|noexcept|noexcept(true)}}, the set is empty.</span>
+
{{rrev|since=c++11|@2@ Otherwise, if the declaration of {{tt|f}}, {{tt|pf}}, or {{tt|pmf}} uses {{rlpt|noexcept|noexcept(true)}}, the set is empty.}}
<li> Otherwise, the set is the set of all types.
+
@3@ Otherwise, the set is the set of all types.
</ol>
+
  
 
Note: for implicitly-declared special member functions (constructors, assignment operators, and destructors){{rev inl|since=c++11| and for the inheriting constructors}}, the set of potential exceptions is a combination of the sets of the potential exceptions of everything they would call: constructors/assignment operators/destructors of non-variant non-static data members, direct bases, and, where appropriate, virtual bases (including default argument expressions, as always)
 
Note: for implicitly-declared special member functions (constructors, assignment operators, and destructors){{rev inl|since=c++11| and for the inheriting constructors}}, the set of potential exceptions is a combination of the sets of the potential exceptions of everything they would call: constructors/assignment operators/destructors of non-variant non-static data members, direct bases, and, where appropriate, virtual bases (including default argument expressions, as always)
  
 
Each expression {{tt|e}} has a ''set of potential exceptions''. The set is empty if {{tt|e}} is a {{rlp|constant expression|core constant expression}}, otherwise, it is the union of the sets of potential exceptions of all immediate subexpressions of {{tt|e}} (including {{rlp|default arguments|default argument expressions}}), combined with another set that depends on the form of {{tt|e}}, as follows:
 
Each expression {{tt|e}} has a ''set of potential exceptions''. The set is empty if {{tt|e}} is a {{rlp|constant expression|core constant expression}}, otherwise, it is the union of the sets of potential exceptions of all immediate subexpressions of {{tt|e}} (including {{rlp|default arguments|default argument expressions}}), combined with another set that depends on the form of {{tt|e}}, as follows:
<ol>
+
@1@ If {{tt|e}} is a function call expression, let {{tt|g}} denote the function, function pointer, or pointer to member function that is that is called, then
<li> If {{tt|e}} is a function call expression, let {{tt|g}} denote the function, function pointer, or pointer to member function that is that is called, then
+
:* if the declaration of {{tt|g}} uses a dynamic exception specification, the set of potential exceptions of {{tt|g}} is added to the set;
<ul>
+
{{rrev|since=c++11|:* if the declaration of {{tt|g}} uses {{rlpt|noexcept|noexcept(true)}}, the set is empty;}}
<li> if the declaration of {{tt|g}} uses a dynamic exception specification, the set of potential exceptions of {{tt|g}} is added to the set;
+
:* otherwise, the set is the set of all types.
<li class="t-rev-inl t-rev-inl-noborder t-since-cxx11"><span>{{mark since c++11}} if the declaration of {{tt|g}} uses {{rlpt|noexcept|noexcept(true)}}, the set is empty;</span>
+
@2@ If {{tt|e}} calls a function implicitly (it's an operator expression and the operator is overloaded, it is a {{rlp|new|new-expression}} and the allocation function is overloaded, or it is a full expression and the destructor of a temporary is called), then the set is the set of that function.
<li> otherwise, the set is the set of all types.
+
@3@ If {{tt|e}} is a {{rlp|throw|throw-expression}}, the set is the exception that would be initialized by its operand, or the set of all types for the re-throwing throw-expression (with no operand).
</ul>
+
@4@ If {{tt|e}} is a {{rlpt|dynamic_cast}} to a reference to a polymorphic type, the set consists of {{lc|std::bad_cast}}.
<li> If {{tt|e}} calls a function implicitly (it's an operator expression and the operator is overloaded, it is a {{rlp|new|new-expression}} and the allocation function is overloaded, or it is a full expression and the destructor of a temporary is called), then the set is the set of that function.
+
@5@ If {{tt|e}} is a {{rlpt|typeid}} applied to a dereferenced pointer to a polymorphic type, the set consists of {{lc|std::bad_typeid}}.
<li> If {{tt|e}} is a {{rlp|throw|throw-expression}}, the set is the exception that would be initialized by its operand, or the set of all types for the re-throwing throw-expression (with no operand).
+
 
<li> If {{tt|e}} is a {{rlpt|dynamic_cast}} to a reference to a polymorphic type, the set consists of {{lc|std::bad_cast}}.
+
{{rrev|since=c++11|@6@ If {{tt|e}} is a {{rlp|new|new-expression}} with a non-constant array size, and the selected allocation function has a non-empty set of potential exceptions, the set consists of {{lc|std::bad_array_new_length}}.}}
<li> If {{tt|e}} is a {{rlpt|typeid}} applied to a dereferenced pointer to a polymorphic type, the set consists of {{lc|std::bad_typeid}}.
+
<li class="t-rev-inl t-rev-inl-noborder t-since-cxx11"><span>{{mark since c++11}} If {{tt|e}} is a {{rlp|new|new-expression}} with a non-constant array size, and the selected allocation function has a non-empty set of potential exceptions, the set consists of {{lc|std::bad_array_new_length}}.</span>
+
</ol>
+
 
{{source|1=
 
{{source|1=
void f() throw(int); // f()'s set is "int"
+
void f() throw(int); // f()'s set is "int"
void g();             // g()'s set is the set of all types
+
void g();           // g()'s set is the set of all types
struct A { A(); };   // "new A"'s set is the set of all types
+
 
struct B { B() noexcept; }; // "B()"'s set is empty
+
struct A { A(); };                 // "new A"'s set is the set of all types
 +
struct B { B() noexcept; };         // "B()"'s set is empty
 
struct D() { D() throw (double); }; // new D's set is the set of all types
 
struct D() { D() throw (double); }; // new D's set is the set of all types
 
}}
 
}}
Line 69: Line 66:
  
 
{{source|1=
 
{{source|1=
struct A {
+
struct A
 +
{
 
     A(int = (A(5), 0)) noexcept;
 
     A(int = (A(5), 0)) noexcept;
 
     A(const A&) throw();
 
     A(const A&) throw();
Line 75: Line 73:
 
     ~A() throw(X);
 
     ~A() throw(X);
 
};
 
};
struct B {
+
 
 +
struct B
 +
{
 
     B() throw();
 
     B() throw();
 
     B(const B&) = default; // exception specification is "noexcept(true)"
 
     B(const B&) = default; // exception specification is "noexcept(true)"
Line 81: Line 81:
 
     ~B() throw(Y);
 
     ~B() throw(Y);
 
};
 
};
 +
 
int n = 7;
 
int n = 7;
struct D : public A, public B {
+
struct D : public A, public B
 +
{
 
     int * p = new (std::nothrow) int[n];
 
     int * p = new (std::nothrow) int[n];
 
     // D has the following implicitly-declared members:
 
     // D has the following implicitly-declared members:
Line 117: Line 119:
 
}
 
}
  
int main() {
+
int main()
  std::set_unexpected([]{
+
{
      std::cout << "That was unexpected!" << std::endl; // flush needed
+
    std::set_unexpected([]
      std::abort();
+
    {
  });
+
        std::cout << "That was unexpected!" << std::endl; // flush needed
  f();
+
        std::abort();
 +
    });
 +
    f();
 
}
 
}
 
  | output=
 
  | output=
Line 130: Line 134:
 
===Defect reports===
 
===Defect reports===
 
{{dr list begin}}
 
{{dr list begin}}
{{dr list item|wg=cwg|dr=1351|std=C++98<br>C++11|before=default argument (C++98) and default member initializer (C++11)<br>were not considered in implicit exception specification|after=made considered}}
+
{{dr list item|wg=cwg|dr=25|std=C++98|before=the behavior of assignment and initialization between pointers<br>to members with different exception specifications is unspecified|after=apply the restriction  for<br>function pointers and references}}
{{dr list item|wg=cwg|dr=2191|std=C++98|before=the set of potential exceptions of a {{tt|typeid}} expression<br>might contain {{tt|bad_typeid}} even if it cannot be thrown|after=contains {{tt|bad_typeid}} only if it can be thrown}}
+
{{dr list item|wg=cwg|dr=1351|std=C++98<br>C++11|before=default argument (C++98) and default member initializer<br>(C++11) were ignored in implicit exception specification|after=made considered}}
 +
{{dr list item|wg=cwg|dr=2191|std=C++98|before=the set of potential exceptions of a {{tt|typeid}} expression<br>might contain {{tt|bad_typeid}} even if it cannot be thrown|after=contains {{tt|bad_typeid}} only<br>if it can be thrown}}
 
{{dr list end}}
 
{{dr list end}}
  

Revision as of 22:46, 7 December 2021

 
 
C++ language
General topics
Flow control
Conditional execution statements
if
Iteration statements (loops)
for
range-for (C++11)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications (until C++17*)
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
const/volatile
decltype (C++11)
auto (C++11)
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
explicit (C++11)
static

Special member functions
Templates
Miscellaneous
 
Exceptions
try block
Throwing exceptions
Handling exceptions
Exception specification
    noexcept specification (C++11)
    dynamic specification (until C++17*)
noexcept operator (C++11)
 

Lists the exceptions that a function might directly or indirectly throw.

Contents

Syntax

throw( ) (1) (deprecated in C++11)(removed in C++20)
throw(typeid, typeid, ...) (2) (deprecated in C++11)(removed in C++17)
1) Non-throwing dynamic exception specification
(until C++17)
1) Same as noexcept(true)
(since C++17)
2) Explicit dynamic exception specification

This specification may appear only on lambda-declarator or on a function declarator that is the top-level(until C++17) declarator of a function, variable, or non-static data member, whose type is a function type, a pointer to function type, a reference to function type, a pointer to member function type. It may appear on the declarator of a parameter or on the declarator of a return type.

void f() throw(int);            // OK: function declaration
void (*pf)() throw (int);       // OK: pointer to function declaration
void g(void pfa() throw(int));  // OK: pointer to function parameter declaration
typedef int (*pf)() throw(int); // Error: typedef declaration

Explanation

If a function is declared with type T listed in its exception specification, the function may throw exceptions of that type or a type derived from it.

Incomplete types, pointers or references to incomplete types other than cv void*, and rvalue reference types are not allowed in the exception specification. Array and function types, if used, are adjusted to corresponding pointer types. parameter packs are allowed(since C++11).

If the function throws an exception of the type not listed in its exception specification, the function std::unexpected is called. The default function calls std::terminate, but it may be replaced by a user-provided function (via std::set_unexpected) which may call std::terminate or throw an exception. If the exception thrown from std::unexpected is accepted by the exception specification, stack unwinding continues as usual. If it isn't, but std::bad_exception is allowed by the exception specification, std::bad_exception is thrown. Otherwise, std::terminate is called.

Potential exceptions

Each function f, pointer to function pf, and pointer to member function pmf has a set of potential exceptions, which consists of types that might be thrown. Set of all types indicates that any exception may be thrown. This set is defined as follows:

1) If the declaration of f, pf, or pmf uses a dynamic exception specification that does not allow all exceptions(until C++11), the set consists of the types listed in that specification.
2) Otherwise, if the declaration of f, pf, or pmf uses noexcept(true), the set is empty.
(since C++11)
3) Otherwise, the set is the set of all types.

Note: for implicitly-declared special member functions (constructors, assignment operators, and destructors) and for the inheriting constructors(since C++11), the set of potential exceptions is a combination of the sets of the potential exceptions of everything they would call: constructors/assignment operators/destructors of non-variant non-static data members, direct bases, and, where appropriate, virtual bases (including default argument expressions, as always)

Each expression e has a set of potential exceptions. The set is empty if e is a core constant expression, otherwise, it is the union of the sets of potential exceptions of all immediate subexpressions of e (including default argument expressions), combined with another set that depends on the form of e, as follows:

1) If e is a function call expression, let g denote the function, function pointer, or pointer to member function that is that is called, then
  • if the declaration of g uses a dynamic exception specification, the set of potential exceptions of g is added to the set;
(since C++11)
  • otherwise, the set is the set of all types.
2) If e calls a function implicitly (it's an operator expression and the operator is overloaded, it is a new-expression and the allocation function is overloaded, or it is a full expression and the destructor of a temporary is called), then the set is the set of that function.
3) If e is a throw-expression, the set is the exception that would be initialized by its operand, or the set of all types for the re-throwing throw-expression (with no operand).
4) If e is a dynamic_cast to a reference to a polymorphic type, the set consists of std::bad_cast.
5) If e is a typeid applied to a dereferenced pointer to a polymorphic type, the set consists of std::bad_typeid.
6) If e is a new-expression with a non-constant array size, and the selected allocation function has a non-empty set of potential exceptions, the set consists of std::bad_array_new_length.
(since C++11)
void f() throw(int); // f()'s set is "int"
void g();            // g()'s set is the set of all types
 
struct A { A(); };                  // "new A"'s set is the set of all types
struct B { B() noexcept; };         // "B()"'s set is empty
struct D() { D() throw (double); }; // new D's set is the set of all types

All implicitly-declared member functions and inheriting constructors (since C++11)have exception specifications, selected as follows:

  • If the set of potential exceptions is the set of all types, the implicit exception specification allows all exceptions (the exception specification is considered present, even though it is inexpressible in code and behaves as if there is no exception specification)(until C++11)is noexcept(false)(since C++11).
  • Otherwise, If the set of potential exceptions is not empty, the implicit exception specification lists every type from the set.
  • Otherwise, the implicit exception specification is throw()(until C++11)noexcept(true)(since C++11).
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 is "noexcept(true)"
    B(B&&, int = (throw Y(), 0)) noexcept;
    ~B() throw(Y);
};
 
int n = 7;
struct D : public A, public B
{
    int * p = new (std::nothrow) int[n];
    // D has the following implicitly-declared members:
    // D::D() throw(X, std::bad_array_new_length);
    // D::D(const D&) noexcept(true);
    // D::D(D&&) throw(Y);
    // D::~D() throw(X, Y);
};

Example

#include <iostream>
#include <exception>
#include <cstdlib>
 
static_assert(__cplusplus < 201700, 
    "ISO C++17 does not allow dynamic exception specifications.");
 
class X {};
class Y {};
class Z : public X {};
class W {};
 
void f() throw(X, Y) 
{
    int n = 0;
    if (n) throw X(); // OK
    if (n) throw Z(); // also OK
    throw W(); // will call std::unexpected()
}
 
int main()
{
    std::set_unexpected([]
    {
        std::cout << "That was unexpected!" << std::endl; // flush needed
        std::abort();
    });
    f();
}

Output:

That was unexpected!

Defect reports

The following behavior-changing defect reports were applied retroactively to previously published C++ standards.

DR Applied to Behavior as published Correct behavior
CWG 25 C++98 the behavior of assignment and initialization between pointers
to members with different exception specifications is unspecified
apply the restriction for
function pointers and references
CWG 1351 C++98
C++11
default argument (C++98) and default member initializer
(C++11) were ignored in implicit exception specification
made considered
CWG 2191 C++98 the set of potential exceptions of a typeid expression
might contain bad_typeid even if it cannot be thrown
contains bad_typeid only
if it can be thrown

See also

noexcept specifier(C++11) specifies whether a function could throw exceptions[edit]