Deprecating Exception Specifications (original) (raw)

Introduction

UK-136

Exception specifications have proven close to worthless in practice, while adding a measurable overhead to programs. The feature should be deprecated. The one exception to the rule is the empty throw specification which could serve a legitimate optimizing role if the requirement to call the runtime unexpected mechanism was relaxed in this case.

As expressed in the national body comment above, exception specifications have not proven useful in practice. There are numerous discussions of the problems with exception specifications in C++ (see, e.g., [Sutter02], [Boost03]), but the main issues are:

In practice, only two forms of exception-throwing guarantees are useful: an operation might throw an exception (any exception) or an operation will never throw any exception. The former is expressed by omitting the exception-specification entirely, while the latter can be expressed as throw() but rarely is, due to performance considerations.

[N3050] introduces a new kind of exception specification, noexcept, the specifies that the function will not throw any exceptions. Unlikethrow(), noexcept does not require the compiler to introduce code to check whether an exception is thrown. Rather, if a function specified as noexcept is exited via an exception, the result is a call to std::terminate().

With the introduction of noexcept, programmers can now express the two kinds of exception guarantees that are useful in practice, without additional overhead. This paper therefore proposes to deprecate "dynamic" exception specifications, i.e., those that are written asthrow(type-id-listopt).

Approach

To aid in the transition from dynamic exception specifications tonoexcept, the wording provides somewhat loose compatibility rules for redeclarations of functions that have exception specifications. Two rules stand out:

  1. All "non-throwing" forms of exception specifications (throw(), noexcept, noexcept(true)) are considered compatible, but the exception specification on the definition is what affects code generation:

// header ultramodern.h void f() noexcept;

// source plodding.cpp #include "ultramodern.h" struct X { }; void f() throw() { // okay, compatible with noexcept throw X(); // calls std::unexpected() }

  1. noexcept(false) is considered compatible with throw( type-id-list ):

// header ultramodern.h void g() noexcept(false);

// source plodding.cpp #include "ultramodern.h" struct X { }; void g() throw(X) { // okay, compatible with noexcept(false) throw X(); // okay }

These compatibility rules allow a gradual migration from dynamic exception specifications to noexcept, since a declaration of a function can choose to use the new or old syntax independently in the declaration and in the definition.

Proposed Changes to Standard Wording

The wording in this paper is based on the current working paper (N3035) as amended by N3050.

15.4 Exception specifications [except.spec]

Modify the paragraphs in this section as follows.

3 If any declaration of a function has an exception-specification that is not a noexcept-specification allowing all exceptions, all declarations, including the definition and an explicit specialization, of that function shall have an compatible exception-specification with the same set of _type-id_s. 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 an compatible exception-specification with the same set of _type-id_s. 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 shallhave the same set of _type-id_s asbe compatible with the _exception-specification_s of other declarations of that function. A diagnostic is required only if the sets of _type-id_s are different_exception-specifications_are not compatible within a single translation unit.

[Insert a new paragraph before paragraph 5] Two_exception-specifications_ are compatible if:

  • both are non-throwing (regardless of their form),
  • both have the form noexcept(_constant-expression_) and the _constant-expression_s are equivalent,
  • one exception-specification is a noexcept-specification allowing all exceptions and the other is of the form throw(_type-id-list_), or
  • both are dynamic-exception-specifications that have the same set of _type-id_s.

5 In such an assignment or initialization, exception-specifications on return types and parameter types shall match exactly be compatible. In other assignments or initializations, exception-specifications shall match exactly be compatible.

[Insert a new paragraph at the end of 15.4] [ Note: The use of _dynamic-exception-specification_s is deprecated (see annex D). - end note ]