6

I have the following code where a variable is being initialized with the result of a function call. This function throws so I set up a try-catch to catch the exception. For some reason the exception is still showing up on the screen even after the catch clause runs.

#include <iostream>
#include <stdexcept>

int f() { throw std::invalid_argument("threw"); return 50; }

struct S
{
    S()
        try : r(f())
    {
        std::cout << "works";
    }
    catch(const std::invalid_argument&)
    {
        std::cout << "fails";
    }

    int r;
};

int main()
{
    S s;
}

This code prints "fails" after showing the exception:

terminate called after throwing an instance of 'std::invalid_argument'
what():  threw

Why is the exception still thrown? I have the same code set up in main and it works without fail:

int main()
{
    try { throw std::invalid_argument("blah"); }
    catch(const std::invalid_argument&) { }
}

So why does it fail when being used in an initializer list?

David G
  • 94,763
  • 41
  • 167
  • 253
  • this is defined in standard somewhere... `{S s; cout< – Bryan Chen Dec 03 '13 at 01:54
  • I think the factor you are throwing in the constructor initialization list is possibly related. You dont get to ctr body if lists throw, so maybe the compiler thinks that catch is part of a body it hasnt yet enabled. I expect is you assign r inside the ctr body and it will behave much more as expected. – RichardPlunkett Dec 03 '13 at 01:58
  • 7
    This is really worth reading: http://www.gotw.ca/gotw/066.htm and so is this: http://stackoverflow.com/questions/7110636/need-for-try-catch-within-a-constructor – Jerry Jeremiah Dec 03 '13 at 01:58
  • Note that the `catch` handler is being entered in your code; if you flush the stream `fails` will be printed. The exception will be rethrown when control reaches the end of the function try block, that's just how they work. – Praetorian Dec 03 '13 at 02:04
  • And this looks useful: http://www.drdobbs.com/introduction-to-function-try-blocks/184401297 – Jerry Jeremiah Dec 03 '13 at 02:07
  • http://www.csci.csusb.edu/dick/c++std/cd2/except.html 15.3 Handling an exception 15.3.16 The exception being handled is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor. Otherwise, a function returns when control reaches the end of a handler for the function-try-block. – Jerry Jeremiah Dec 03 '13 at 02:13

2 Answers2

14

Constructors with function try blocks (like what you have for S) automatically rethrow any exceptions caught by the catch block. Consequently, after the catch catches the exception, it rethrows it. This behavior is different from normal catch handlers, which don't do this. I think the rationale is that if construction of a data member or base class fails, the object has failed to construct. The purpose of the catch handler is just to do any extra cleanup before the exception propagates outward.

Hope this helps!

templatetypedef
  • 362,284
  • 104
  • 897
  • 1,065
2

From the C++11 Standard, 15.3/15:

The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor.

The reasons are well explained in the GOTW Jerry links under your question, but summarily: imagine if it hadn't rethrown, then the next lines after S s; will presumably attempt to use s despite it never having completed construction, and when s leaves scope the constructor will have arranged a call to the destructor for s - potentially freeing never-initialised pointers, releasing never-taken locks etc..

By way of contrast, if you let the data member be default initialised then assign to it from a try/catch block in the constructor body, the state of the object including bases and data members can potentially be kept in some coherent state: it's up to you as the programmer to decide whether that state's ok - i.e. whether you'll use the try/catch block inside the constructor body, and have later member functions handle a potentially default-constructed data member.

Tony Delroy
  • 102,968
  • 15
  • 177
  • 252