Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share
menu search
person
Welcome To Ask or Share your Answers For Others

Categories

The C++ Standard states the following about virtual functions that have exception specifications:

If a virtual function has an exception-specification, all declarations, including the definition, of any function that overrides that virtual function in any derived class shall only allow exceptions that are allowed by the exception-specification of the base class virtual function (C++03 §15.4/3).

Thus, the following is ill-formed:

struct B {
    virtual void f() throw() { } // allows no exceptions
};
struct D : B {
    virtual void f() { }         // allows all exceptions
};

(1) Does this rule apply to destructors? That is, is the following well-formed?

struct B {
    virtual ~B() throw() { }
};
struct D : B {
    virtual ~D() { }
};

(2) How does this rule apply to an implicitly declared destructor? That is, is the following well-formed?

struct B {
    virtual ~B() throw() { }
};
struct D : B { 
    // ~D() implicitly declared
};

While in the general case one should never write an exception specification, this question has practical implications because the std::exception destructor is virtual and has an empty exception specification.

Since it is good practice not to allow an exception to be thrown from a destructor, let's assume for the sake of simplifying any examples that a destructor either allows all exceptions (that is, it has no exception specification) or it allows no exceptions (that is, it has an empty exception specification).

See Question&Answers more detail:os

与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
241 views
Welcome To Ask or Share your Answers For Others

1 Answer

(1) Does this rule apply to destructors?

Yes, this rule applies to destructors (there is no exception to the rule for destructors), so this example is ill-formed. In order to make it well-formed, the exception specification of ~D() must be compatible with that of ~B(), e.g.,

struct B {
    virtual ~B() throw() { }
};
struct D : B {
    virtual ~D() throw() { }
};

(2) How does this rule apply to implicitly declared special member function?

The C++ Standard says the following about implicitly declared special member functions:

An implicitly declared special member function shall have an exception-specification.

If f is an implicitly declared default constructor, copy constructor, destructor, or copy 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 (C++03 §15.4/13).

What functions are directly invoked by an implicitly declared destructor?

After executing the body of the destructor and destroying any automatic objects allocated within the body, a destructor for class X calls

  • the destructors for X’s direct members,
  • the destructors for X’s direct base classes and,
  • if X is the type of the most derived class, its destructor calls the destructors for X’s virtual base classes

(C++03 §12.4/6; reformatted for easier reading).

So, an implicitly declared destructor has an exception specification that allows any exceptions allowed by any of those destructors. To consider the example from the question:

struct B {
    virtual ~B() throw() { }
};
struct D : B { 
    // ~D() implicitly declared
};

The only destructor called by the implicitly declared ~D() is ~B(). Since ~B() allows no exceptions, ~D() allows no exceptions and it is as if it were declared virtual ~D() throw().

This exception specification is obviously compatible with ~B()'s, so this example is well-formed.


As a practical example of why this matters, consider the following:

struct my_exception : std::exception {
    std::string message_;
};

~string() allows all exceptions, so the implicitly declared ~my_exception() allows all exceptions. The base class destructor, ~exception(), is virtual and allows no exceptions, so the derived class destructor is incompatible with the base class destructor and this is ill-formed.

To make this example well-formed, we can explicitly declare the destructor with an empty exception specification:

struct my_exception : std::exception {
    virtual ~my_exception() throw() { }
    std::string message_;
};

While the rule of thumb is never to write an exception specification, there is at least this one common case where doing so is necessary.


与恶龙缠斗过久,自身亦成为恶龙;凝视深渊过久,深渊将回以凝视…
thumb_up_alt 0 like thumb_down_alt 0 dislike
Welcome to ShenZhenJia Knowledge Sharing Community for programmer and developer-Open, Learning and Share

548k questions

547k answers

4 comments

86.3k users

...