7

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.

jasonline
  • 8,646
  • 19
  • 59
  • 80
  • 2
    I sincerely hope you never throw instances of classes that are not meant to be used as exceptions and this is merely for a quick and dirty illustration of your question :) – Matthieu M. Mar 05 '10 at 18:00
  • Matthieu: Ah, yes of course. I understand. – jasonline Mar 06 '10 at 01:25

2 Answers2

14
  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).

C. K. Young
  • 219,335
  • 46
  • 382
  • 435
  • 1
    For Herb Sutter's take on this (which agrees with Chris's), see http://www.gotw.ca/publications/mill22.htm –  Mar 05 '10 at 15:12
  • 1
    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 Mar 05 '10 at 15:12
  • @Neil: Thanks for the link; I've changed my link (originally a GotW one) to use it. :-) – C. K. Young Mar 05 '10 at 15:15
1

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
  • 2,733
  • 2
  • 17
  • 10