Home > C++, computing, software > In C++ throw is an expression

In C++ throw is an expression

After 15 years programming in C++, I was surprised to discover today that throw in C++ is an expression rather than a statement. As a result, throw may be used as part of larger expressions.

int x = 5;
int y = x > 4 ? x : throw std::out_of_range;

The only use I’ve found for this — and in fact the speculative attempt by which I discovered it — is range checking within constructor initializer lists.

RangeChecked::RangeChecked(int x) :
    int_member(x > 0 ? x : throw std::out_of_range)
{
}

It’s academic what the type of a throw expression is, since it will never be returned, but for type-checking purposes the compiler seems to be happy to use it in place of any type.

Categories: C++, computing, software Tags:
  1. jlouis
    July 31st, 2009 at 13:42 | #1

    Most functional languages with built-in exceptions have their exception-raising construct as an expresssion. The typing of such expressions are not that hard. Basically “throw e” for some expression e, returns whatever type fits into the remaining expression. Ie, the throw is parametric for any type t (think, perhaps, of throw as a template instantiated with the correct return type).

    Another approach is the one Haskell takes, where the throw, and the context is operating inside a monad, but it is farther away from the C++ semantics.

  2. Robert Smallshire
    July 31st, 2009 at 14:51 | #2

    There are some claims over on Reddit, that use of this within a constructor initializer may be bad practice. The reason is that C++ refuses to call destructors for objects that are partially constructed. According to More Effective C++ “you must design your constructors so that they clean up after themselves. Often, this involves simply catching all possible exceptions, executing some cleanup code, the rethrowing the exception so it continues to propagate”.

    I disagree that exceptions shouldn’t be thrown from constructors, and take the view that exceptions are the best way to signal constructor failure. Each member that has already been constructed should have its destructor called, so it is not an issue, unless the data members are unmanaged, which is usually bad practice anyway.

    It doesn’t make sense for the destructor to be called on a partially constructed object, which is why the destructor will never be run unless the constructor ran to completion. If any manual cleanup is needed, it can be handled, even from a exception originating in the constructor initializer list, by use of a function-try block, but at that point any concision gained by not moving checking into the constructor body and assigning to the data members has probably been lost.

  3. Tom Ritchford
    July 31st, 2009 at 17:46 | #3

    “exceptions are the best way to signal constructor failure”.

    I do not agree (with almost 20 years of experience in the language).

    Constructors and destructors are two of the most dangerous places in the language – because tons of “invisible code” is called, constructors of parts or of parents that’s not represented in code at all but created by the compiler.

    There are a lot of arcane details there, relating for example to the creation and destruction order (particularly of static objects). Best to not even have to think about it.

    As a rule, I do as absolutely little as possible in an object’s constructor. This rule usefully applies to every language I write in, it seems! (Well… it seems less important in Python, but then everything there seems really light anyway…)

    If you need to validate parameters, do that in a factory function. Factory functions have a lot of advantages over C++ constructors anyway: you can have multiple factory functions with the same signature, you can give them self-documenting names, and most important, they’re easy to mock where constructors cannot be.

    In general, it’s a bad idea to do something that might confuse the next person without a really good reason. When I’m debugging a system that’s displaying unusual behavior, if I see

    RangeChecked::RangeChecked(int x) : int_member(x > 0 ? x : throw std::out_of_range) {}

    I immediately spend a few minutes thinking it through and probably searching the documentation. And I’d worry in the back of my head that this might not work under a different compiler.

    Even a comment would require me to read it and ask myself if I really believed it.

    It’s a small cognitive load on each later programmer – for zero benefit. Keep it simple!

  4. Paercebal
    July 31st, 2009 at 22:03 | #4

    @Tom

    Try this link for questions about destruction in partially constructed objects:

    http://stackoverflow.com/questions/188693/is-the-destructor-called-if-the-constructor-throws-an-exception/188882

    What’s happen in constructors/destructor is usually easy to understand (even if it can get confused when dealing with multiple inheritance). The order of member variables construction/destruction is known (i.e. the order of their declaration in the class/struct, or its exact reverse for destruction).

    The “do nothing in constructor/destructor” is a pattern usually seen in C coders who came to C++, and of course, it is a wrong pattern.

  5. August 1st, 2009 at 03:24 | #5

    You can also use this behavior for null checks

    SomeClass* ptr = foobar->Baz || throw “Failed!”;

  6. Robert Smallshire
    August 1st, 2009 at 08:42 | #6

    @Paul Betts Very concise! This reminds me of the construct in Perl

    open(FOO,$foo) || die "Can't open $foo: $!";

  7. August 1st, 2009 at 14:22 | #7

    Good to know this. But I hardly use the throw/catch in C++, unlike in Java

  8. Yury
    August 1st, 2009 at 20:01 | #8

    >>It’s academic what the type of a throw expression is, since it will never be returned, but for type-checking purposes the compiler seems to be happy to use it in place of any type.

    throw-expression is of type void actually (see 15/1). What happens is special treatment of throw-expression in conditional-expression as per 5.16/2. I don’t think this is that academic but rather to save some typing and have:

    int y = x > 4 ? x : throw std::out_of_range (“”) ;

    instead of:

    int y = x > 4 ? x : (throw std::out_of_range (“”), 0) ;

    which still would be necessary if you decide to call void-returning function instead of raising an exception.

  9. August 3rd, 2009 at 22:12 | #9

    @Paercebal I agree 110%, I do as much work as I possibly can in my initializer list and the rest in the constructor. In fact, doing as much work as you can in the constructor is a fundamental premise of C++: RAII.

  1. October 31st, 2013 at 19:42 | #1