tags:

views:

108

answers:

5

Hello all,

I saw the following snippet code:

class Foo
{
public:
        void virtual func() throw (int, float) = 0;
};

class Bar : public Foo
{
public:
        void virtual func() throw(short);      // line 1: compile error "
                                                                      // looser throw specifier"
        void virtual func() throw();                // line 2: can compile
        void virtual func() throw(float, int); // line 3: can compile
        void virtual func() throw(float);        // line 4: can compile
        void virtual func() throw(int);           // line 5: can compile

};

int main(void)
{
        return 1;
}

Q1> What is meaning of

void virtual func() throw (int, float) = 0;

Q2> why line1 cannot pass the compiler?

Thank you

+6  A: 

You're defining the same function signature many times. The different throw() qualifiers are not enough to disambiguate the functions.

The throw() qualifier simply means that the specified function is only expected to throw the types listed in the parenthesis following the qualifier. However, this does not actually prevent the function from throwing. Rather, if the function actually does throw any unlisted types, your program will terminate.

Charles Salvia
That's backwards. If the function throws any of the listed types, the exception can be caught and handled. It's types NOT listed that will cause the program to terminate (via `std::unexpected`).
Ben Voigt
Right - fixed it.
Charles Salvia
+2  A: 

The meaning of named throw statements is to declare that a function can only throw those named exceptions either directly or indirectly.

So the line:

void virtual func() throw(int, float) =0;

means that whatever class inherits this base type it is only allowed to throw either an int or a float. It can not either directly or indirectly throw any other type of exception or object. If it does, it will call the unexcepted() function. By default this calls the terminate() function. You're allowed to reset that using the set_unexpected function but still.

By choosing to add those throw statements to your interface you're really limiting yourself.

wheaties
Actually if the function breaks its contract, `std::unexpected` will be used instead of the thrown value. A comment wouldn't do that.
Ben Voigt
Actually reading it the way I wrote it is very misleading. Hold on a sec.
wheaties
typo: unexcepted vs unexpected
Ben Voigt
+3  A: 

The function you're defining in the base class is making a guarantee - it can only throw an int or a float. Your line 1 is failing because it's breaking the guarantee by saying it will throw a short, which isn't either of the above.

The = 0 in Q1 declares that each derived class that you try to create an instance of will need to provide its own declaration and implementation of this function. The base class may also provide an implementation, but typically it doesn't.

Mark Ransom
The pure modifier `= 0` doesn't prevent having a concrete implementation in the base class.
Ben Voigt
@Ben, of course you're right. I'll fix my answer.
Mark Ransom
+2  A: 

When you override a virtual function any exception specifier that you provide must be at least as restrictive as that specified on the function that you are overriding. This prevents the base class' exception specification from being violated.

As the base class' exception specifier [throw (int, float)] does not allow a short to be thrown, the derived class cannot allow a short to be thrown. At most it may allow an int and/or a float; it may allow only either one or neither to be thrown as any of these possibilities would be more restrictive than the exception specification of the base class function.

Charles Bailey
+3  A: 

Let's break this down. The declaration:

void virtual func() throw (int, float) = 0;

has 2 constructs that you're asking about. the =0 construct tells the compiler that the declared function is 'abstract', which tells the compiler that the function need not be defined in the class Foo (though it can be - but it usually isn't) and that an object of class Foo cannot be directly created - either as a local, global or via new. However, you can have pointers or references to objects of class Foo. Some derived class needs to override the function as a non-abstract function - objects of that class can be directly created (as long as there are no other abstract functions that haven't been made 'concrete').

The throw (int, float) construct is an exception specifification. This tells the compiler that the function's contract is that it will only throw exceptions of type int or float if it throws an exception. If the function throws some other kind of exception, the compiler is obligated to handle that specially (by calling std::unexpected()).

Now, if you try to override that function in a derived class with the following declaration:

void virtual func() throw(short);

You're saying that the function's contract is that it will throw exceptions of type short if an exception is thrown. However, throwing short is not part of the contract of the function being overridden, so the compiler doesn't permit it.

If you declare the override like so:

void virtual func() throw(float);

You're saying that the override can throw a float, which is part of the contract of the original declaration (if it never throws an int that doesn't break the contract - the original contract only says that the function is allowed to throw an int, not that it has to).

The relevant part of the standard is 15.4/3 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.

Note that the standard explicitly states that an exception specification is not part of the function's type (15.4/12), so a function pointer can, for example, point to functions that have different exception specifications.

Michael Burr
Hello Michael, Thank you for your excellent answer.
q0987