8

I can do this, no problem:

long lngval = 3L;
int i = lngval;

but if I try this:

try {
  throw 3L;
}

catch(int i) {
  cout << "caught " << i << endl;
}

I get an unhandled exception.

This seems inconsistent. what is the reason for no type conversion in this case?

CharlesB
  • 86,532
  • 28
  • 194
  • 218
Angus Comber
  • 9,316
  • 14
  • 59
  • 107
  • 7
    Write 100 times: "I will throw only objects that derive from std::exception" – Tadeusz Kopec for Ukraine Sep 27 '11 at 14:24
  • 1
    @Tadeusz Except with the common idiom of throwing an `int` to terminate a program. (`main` contains a `try...catch` to catch the `int` and return it. This idiom has the same effect as calling `exit`, **except** that destructors of all local variables are called.) – James Kanze Sep 27 '11 at 15:17
  • @JamesKanze: interesting, I didn't know about this idiom. I did see a `SuicideException` that inherited from nothing, once, I'll you guess the role. In both cases though you really on the fact that no-one ever wrote a `catch(...)` clause. – Matthieu M. Sep 27 '11 at 18:21
  • @JamesKanze: I heard about that idiom, but I don't think it is common. What would be a situation where you want half-immediate exit? If you can't handle an exception, don't catch it. And if you can, well, catch it. – Sebastian Mach Sep 27 '11 at 19:36
  • @phresnel I don't know that the idiom is that common, although I often use it myself. It **is** a valid case where one would throw an `int`, however. (About the only one I can think of, to tell the truth.) – James Kanze Sep 28 '11 at 07:24
  • @Matthieu Obviously, you do count on the fact that no one is simply absorbing all exceptions. (A `catch (...)` isn't a problem **if** it rethrows.) Practically, I can't think of a case where one would want to completely ignore all exceptions. – James Kanze Sep 28 '11 at 07:26
  • @JamesKanze: I cannot fathom it either, but I've seen it :( – Matthieu M. Sep 28 '11 at 09:33
  • @JamesKanze: But which is that valid case you mention? – Sebastian Mach Sep 29 '11 at 04:48
  • @phresnel throwing an `int` in order to terminate a program. It's the exception to the rule that everything you throw should derive from `std::exception`. – James Kanze Sep 29 '11 at 07:35
  • @JamesKanze: How shall I say ... I don't get why it should be good to "immediately exit" a program at all. Read: I don't know a single reason why `throw ` is justified in any case. I got that you talk about terminating like that, but WHY should one terminate like that? That was my question. I understand you can use `throw ` if you want the clean C++ variant of `exit`. But I don't get _why_ you should ever need it. In case of error, throw something appropriate, then either catch or don't. In case you want to `return`, just `return`. And because it is not common, it will confuse ppl. – Sebastian Mach Sep 29 '11 at 08:49
  • @JamesKanze: And if you need that to break out of some big hierarchy, I guess `throw ` is just band-aid for a broken hierarchy and control flow. – Sebastian Mach Sep 29 '11 at 08:51
  • @phresnel It's called a fatal error. There are errors (not programming errors like assertion failures, but external conditions) which mean that the program can't continue, or that it makes no sense for it to continue. – James Kanze Sep 29 '11 at 08:56
  • @James Kanze: I see, though there is still the danger that non-RAII resources are not freed up. I don't agree on not using RAII resources, but they are part of many projects. – Sebastian Mach Sep 29 '11 at 09:27

5 Answers5

8

In the first case, the compiler can tell exactly what you want to do: convert a long to an int. In the second case, the compiler has to assume that you might have a construct like this:

try {
  try {
    throw 3L;
  }
  catch (int i) { /* P */ }
}
catch (long l) { /* Q */ }

The idea is that the compiler can never know whether there might be a catch (long l) lurking outside the current context, so it's never safe to just pick the first possible conversion.

This is also why it's common to use a class hierarchy when throwing exceptions rather than random types like int or long: it makes it easy to add more or less specification to your exception handlers in such a way that the compiler can be sure of your intentions (via the is-a relationship).

ladenedge
  • 13,197
  • 11
  • 60
  • 117
4

catch does not necessarily need the exact type.

It is common and good practice to use exceptions derived from std::exception (found in <stdexcept>). The reason is that you can then catch polymorphically, i.e. you do not need to know the exact type (see also Difference: std::runtime_error vs std::exception()) and that we have a convention to handle this.

Either you use one of the ones provided by the standard (e.g. std::range_error), or if nothing suits your problems [enough], specialize std::exception:

#include <stdexcept>

class moores_law_stopped : public std::exception {
public:
    virtual ~moores_law_stopped() throw() {} 
    virtual const char *what() const throw() { 
        return "moores law stopped. duck under your table.";
    }
};



#include <iostream>
int main () {
    try {
        throw moores_law_stopped();
    } catch (std::exception const &e) {
        std::cerr << "oh oh: " << e.what() << std::endl;
    }
}

Output:

oh oh: moores law stopped. duck under your table.

The convention is to catch by reference or const reference, so that you get polymorphic behavior without fearing object slicing.

Community
  • 1
  • 1
Sebastian Mach
  • 38,570
  • 8
  • 95
  • 130
1

The catch statement catches an object (or a scalar variable in your cases) given its type, so if the type mismatch, it passes to the next catch statement (if there is one) or to the default exception receiver.

In your case, you could have a second catch statement catching long, and maybe somewhere else, so your catch statement won't catch anything.

To catch any exception, just use catch() {} :)

A single tip, better use exception class, or subclass it for you own need :)

Jaffa
  • 12,442
  • 4
  • 49
  • 101
1

You can also throw 3; - no problem.

int and long are different types. It's an advantage of the exception handling that you can tell the exceptions apart from looking at their type (one central try block can handle exceptions of various kinds from various places / a try block can handle just some kinds of exceptions, letting others propagate).

Also, it is recommended to throw one of the standard exceptions or derive a class from one of those. Then you could just catch (const std::exception&) if you just want to handle the exception and don't care about the particular type.

UncleBens
  • 40,819
  • 6
  • 57
  • 90
0

You can catch multiple types in one try-catch block. In order for the compiler to know which catch block to throw to, it must be able to match the exact type. Or it can have a default catch block -- catch (...) {}, but you won't be able to get at the value thrown in that case.

Jerry Coffin
  • 476,176
  • 80
  • 629
  • 1,111
Mark Jones
  • 402
  • 2
  • 5