views:

296

answers:

2

Consider the following code:


class A
{
public:
    virtual void f() throw ( int ) { }
};

class B: public A
{
public:
    void f() throw ( int, double ) { }
};

When compiled, it says that derived class B has a looser throw specifier compared to A. What is the importance of this? If we try to exchange their exception specification, such that A::f() throws int and double while B::f() throws only int, the error does not appear.

+14  A: 
  1. Don't use exception specifications in C++. It's very counter-intuitive compared to, say, Java's ones.
  2. Having a wider specification in the derived class breaks LSP (Liskov Substitution Principle).

To expand on point 2: A's callers expect that only int comes out, but if you use a B (which, because it's publicly derived from A, also means it's usable as an A), suddenly double can come out too, and that would break A's contract (that only int gets thrown).

Chris Jester-Young
For Herb Sutter's take on this (which agrees with Chris's), see http://www.gotw.ca/publications/mill22.htm
anon
In fact, C++ even checks for this in implicit function declarations (for functions it directly calls from those, their exception spec includes the called function's specs): `struct A { virtual ~A() throw(); }; struct B { ~B() throw(int); }; struct C : A, B { }; /* error: ~C() has throw(int), but ~A() has throw()! */`
Johannes Schaub - litb
@Neil: Thanks for the link; I've changed my link (originally a GotW one) to use it. :-)
Chris Jester-Young
A: 

Your B violates the Liskov Substitution Principle - eg:

void foo(A* a) throw() // ie I will not throw
{
  try
  {
     a->f();
  }
  catch(int)
  {}
}

This is valid according to the interface for A; in particular, we do not expect a double to be thrown. But consider if we were to call

foo(new B)

with the interface you describe, and

B::f()
were to throw a double.

tragomaskhalos