问题
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).
回答1:
(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-idT
if and only ifT
is allowed by the exception-specification of a function directly invoked byf
’s implicit definition;
f
shall allow all exceptions if any function it directly invokes allows all exceptions, andf
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 forX
’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.
来源:https://stackoverflow.com/questions/3233078/how-does-an-exception-specification-affect-virtual-destructor-overriding