9

Suppose I have:

class Foo
{
public:
   virtual ~Foo()=default;
};

What is the exception-specification on the defaulted destructor? Is the defaulted destructor equivalent to:

   virtual ~Foo() {};
or
   virtual ~Foo() throw() {};
or
   virtual ~Foo() noexcept {};

Section 15.4 of the C++11 standard says it depends on the exception specifications of the functions directly invoked by the destructor's implicit definition. In this case there are no members, and no base classes, so AFAIK there are no functions directly invoked by the implicit destructor. Is this an ambiguity (or omission) in the standard?

It matters, of course, because if it implicitly has throw(), then all subclasses must declare their destructors with throw(). Don't tell me it's a bad idea to throw exceptions in destructors, I know that. I deal with lots of legacy code where exception specs were not used at all.

As a point of information, when I tried:

class SubFoo : public Foo
{
public:
   virtual ~SubFoo();
};

I got an error (mismatched exception specs) in GCC 4.4 (although I admit I may not have had the right command line switches), but not in XCode 4.3 using the "11" compilers.

user1414050
  • 99
  • 1
  • 2

2 Answers2

4

Back up to earlier in the same sentence (§15.4/14):

...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;..."

Therefore, if ~Foo doesn't invoke any functions, it has an implicit declaration that allows no exceptions to be thrown.

According to §15.4/3:

Two exception-specifications are compatible if:

  • both are non-throwing (see below), regardless of their form,

That's the case here, so it doesn't really matter whether the declaration is throw() or noexcept -- the two are compatible in any case.

Community
  • 1
  • 1
Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
  • Thanks for the answer. To follow up a bit more, the spec says "A declaration of a destructor that does not have an exception-specification is implicitly considered to have the same exception-specification as an implicit declaration". So then "virtual ~SubFoo();" (without an exception spec) is not ill-formed, unless it directly invokes functions that implicitly or explicitly permit throwing? – user1414050 May 24 '12 at 05:25
  • @user1414050: I believe that's correct, yes -- but we're getting into shadowy enough corners of the language that I wouldn't bet a lot on most compilers getting it right yet. In particular, I suspect many (most?) compilers treat `virtual ~SubFoo();` as meaning "can throw anything", just like it would with a normal function. – Jerry Coffin May 24 '12 at 05:35
  • I think this conclusion may not hold, since the "if and only if" is contradicted by the following part-sentence about allowing all exceptions (i.e., the standard is self-contradicting here). And I think it's questionable if the intention is that destructors have no-throw by default. Because that would be incompatible with C++03. – Cheers and hth. - Alf May 24 '12 at 07:42
  • 1
    I was surprised to see that they did intend for the exception specifications of destructors to be different than C++03. See Appendix C.2.5 – user1414050 May 25 '12 at 19:23
2

The standardese starts nicely in C++11 §8.4.2/2,

If a function is explicitly defaulted on its first declaration,
— it is implicitly considered to be constexpr if the implicit declaration would be,
— it is implicitly considered to have the same exception-specification as if it had been implicitly declared (15.4), …

But then, over in C++11 §15.4/14, the logic rapidly devolves,

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.

In the standard's meaning of "allow" it is about explicitly allowing, through an exception specification.

If f calls two functions, one of which specifies and therefore allows T, and one of which allows all exceptions, then f must both specify T and allow all exceptions, which isn’t possible.

So this definitely looks like a defect in the standard.

I found a related Defect Report, http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_active.html#1351.

However, it looks like this area is just a Big Mess. :-(

Cheers and hth. - Alf
  • 142,714
  • 15
  • 209
  • 331
  • @anonymous downvoter: please explain your downvote so that others can learn from your insight(s). – Cheers and hth. - Alf May 24 '12 at 07:36
  • Doesn't look like a DR to me; it just means that you have to consider `exception specification specifies T` as a subset of `allow all exceptions`. – MSalters May 24 '12 at 09:52
  • @MSalters: there's no syntax for that? – Cheers and hth. - Alf May 24 '12 at 13:01
  • 1
    @Alf: If "any of the functions directly invoked" "allows all exceptions", then the set of exceptions types `T` allowed is the infinite set of all types, and therefore the implicit exception-specification must also "allow all exceptions". Alternative proof by contradiction: if the implicitly-declared exception specification disallowed `U`, then (_if and only if_) it follows that none of the directly invoked functions may allow `U`. This violates the premisse that at least one function allows all exceptions, including `U`. – MSalters May 24 '12 at 13:31
  • @MSalters: I think I do understand what you think the text is saying, and that may well have been the *intention*. However, the text talks about "specifies" `T` in an exception-specification. Specifying `T` is not an abstract allow/disallow, it is to write out `T` in a `throw( T, ... )`, which would need to be infinite, which is impossible, in order to allow all exceptions. – Cheers and hth. - Alf May 25 '12 at 00:53
  • @Alf: Since the compiler doesn't physically write out the exception specification, and implementing it is trivial, I don't see a problem with an infinitely-large excception specification. – MSalters May 25 '12 at 07:09
  • @Alf: Your statement that _specifying `T` is not an abstract allow/disallow, it is to write out `T` in a `throw (T,...)`_ is incorrect. The definition of _exception specification_ includes not only the `throw` syntax, but the `noexcept` syntax as well (15.4/1). Hence the _syntax_ your are looking for is `noexcept(false)`. That includes `T` as well as the infinity of other possible exceptions. (And of course that is _semantically_ equivalent to not making any specification at all.) – jogojapan Jul 16 '12 at 03:06
  • @jogojapan: i suspect you're into circular logic here, assuming the desired conclusion in order to conclude that it follows. – Cheers and hth. - Alf Jul 16 '12 at 07:21