Default destructor nothrow
Asked Answered
M

1

34

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.

Margertmargery answered 16/7, 2012 at 1:50 Comment(3)
Most probably a bug in the compiler if your code is exactly like the one in the question.Shulem
FYI g++ snapshot 20120708 compiles the code properly.Shelf
great question, bumped exactly into this today when trying to port something on Debian g++4.7Volunteer
S
39

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.

Styptic answered 16/7, 2012 at 3:27 Comment(1)
Nice answer, the problem is actually quite important, as you may declare (as I did) a singleton with a =default protected destructor using the CRTP pattern, and then all g++ compilers <= 4.7 will spit out the same error about "looser throw..." However for g++ >= 4.8, the code compiles just fine. My solution to portable code is to just write the destructor as virtual ~Destructor(){};. Not elegant, but works on g++ 4.7 and higher versions.Volunteer

© 2022 - 2024 — McMap. All rights reserved.