How does an exception specification affect virtual destructor overriding?

前端 未结 1 1755
不思量自难忘°
不思量自难忘° 2020-12-05 19:19

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

If a virtual function has an exception-specifica

相关标签:
1条回答
  • 2020-12-05 20:03

    (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.

    0 讨论(0)
提交回复
热议问题