Kan Li
Kan Li

Reputation: 8797

Default destructor nothrow

The following code doesn't compile under gcc-4.7.1 but compile under clang-3.2. Which one follows the C++11 standard?

struct X {
  virtual ~X() = default;
};

struct Y : X {
  virtual ~Y() = default;
};

gcc-4.7.1 complains that:

looser throw specifier for 'virtual Y::~Y()'
error: overriding 'virtual X::~X() noexcept(true)'

Obviously, gcc-4.7.1 thinks X's default destructor nothrow, but Y's default destructor is not nothrow. Why is this? Can anyone refer to the correct place in standard? Thanks.

I saw similar questions on stackoverflow, but I didn't see the answers refering to the standard.

Upvotes: 34

Views: 4287

Answers (1)

jogojapan
jogojapan

Reputation: 70027

The compilers are caught in a dilemma here, for the following reasons:

(1) Not specifying any exceptions in a function declaration (i.e. not using throw nor noexcept (which is equivalent to noexcept(true) )) means to allow that function to throw all possible exceptions:

(§15.4/12, emphasis mine) A function with no exception-specification or with an exception-specification of the form noexcept(constant-expression) where the constant-expression yields false allows all exceptions. [...]

(2) A default destructor must allow exactly the exceptions that are allowed by the functions directly invoked by its implicit definition:

(§15.4/14, emphasis mine) An implicitly declared special member function (Clause 12) shall have an exception-specification. If f is an implicitly declared default constructor, copy constructor, move constructor, destructor, copy assignment operator, or move assignment operator, its implicit exception-specification specifies the type-id T if and only if T is allowed by the exception-specification of a function directly invoked by f’s implicit definition; f shall allow all exceptions if any function it directly invokes allows all exceptions, and f shall allow no exceptions if every function it directly invokes allows no exceptions.

(3) When a special member (such as a destructor) is explicitly defaulted, i.e. when you use = default, the exception specification is optional (see the use of "may have" below):

(8.4.2/2, emphasis mine) An explicitly-defaulted function [...] may have an explicit exception-specification only if it is compatible (15.4) with the exception-specification on the implicit declaration. [...]

There is no statement in the Standard that requires the exception specification in an explicitly defaulted destructor.

Conclusion: Therefore, not specifiying the exceptions of an explicitly defaulted destructor can be interpreted in two ways:

  • To mean that all exceptions are allowed (according to (1) above)
  • Or, alternatively, to mean that exactly the same exceptions are allowed as are allowed by the implicit default definition of the destructor (according to (3) above), which in your case means to allow no exceptions (according to (2) above).

Unfortunately, GCC resolves this dilemma in one way (in favor of "no exceptions") in the case of your base class declaration, and in a different way in the case of the derived class (in favor of "all exceptions" there).

I believe the most natural interpretation of this admittedly ambiguous situation is to assume that (2) and (3) override (1). The Standard does not say so, but it should. Under that interpretation, Clang seems to be right here.

Upvotes: 39

Related Questions