For the following code:
#include <iostream>
struct Str
{
Str() { throw 100; }
};
class Cla
{
public:
Cla()
try : m_mem() { }
catch(...)
{
std::cout << "Catch block is called"<< std::endl;
}
private:
Str m_mem;
};
int main()
{
Cla obj;
}
I tried to catch the exception in the catch block. But after the catch block is run, the system still calls std::terminate to terminate the program. I didn't re-throw the exception in the catch block, can you tell me why the system crashes? Thanks!
Here is a test on compiler explorer: https://godbolt.org/z/74sTcxrY4
You're contradicting yourself.
I tried to catch the exception in the catch block. But after the catch block is run
If the catch block is run, you succeeded catching the exception from the initializer list. You're just surprised by what happened next.
First, let's think about what happens when a constructor throws: the object is not constructed. Right? the constructor never finished setting it up, so you can't use it for anything.
int main() {
Cla obj; // member subobject constructor throw, but we caught it!
obj.print(); // but we still can't use this here, because the constructor never completed
}
So it doesn't really make sense to let you swallow the exception here. You can't handle it, because you can't go back and re-try constructing your member and base-class subobjects. If you don't have a properly-constructed object, the only way C++ has of dealing with that is to unwind the block scope in which that object would otherwise be assumed to be ... well, a real object.
Hence, per the documentation:
Every catch-clause in the function-try-block for a constructor must terminate by throwing an exception. If the control reaches the end of such handler, the current exception is automatically rethrown as if by throw;. The return statement is not allowed in any catch clause of a constructor's function-try-block.
... and equivalently in the standard (draft)
The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor.
Otherwise, flowing off the end of the compound-statement of a handler of a function-try-block is equivalent to flowing off the end of the compound-statement of that function (see [stmt.return])
There is no way to swallow an exception in a constructor's function catch block. It will necessarily throw something, the caught exception if control reaches the end of the catch block. The only way around this is to std::terminate or otherwise abort before then.
From cppreference:
Every catch-clause in the function-try-block for a constructor must terminate by throwing an exception. If the control reaches the end of such handler, the current exception is automatically rethrown as if by throw;. The return statement is not allowed in any catch clause of a constructor's function-try-block.
This is intentional and beneficial. It would not make sense to return from a constructor that did not complete, it would allow an object to be accessible despite not actually having been created.
Such a catch block can be used to log errors, release resources (if you aren't using RAII) and perform other similar cleanup. But it can't prevent an exception from propagating.
Related
Consider this artificial example.
Two identical destructors, one catches the exception inside function style try/catch, one scope based try/catch.
#include <iostream>
struct A {
~A() noexcept try {
throw std::runtime_error("~A");
} catch (std::exception const &e) {
std::cout<<__LINE__<<" "<<e.what()<<std::endl;
}
};
struct B {
~B() noexcept {
try {
throw std::runtime_error("~B");
} catch (std::exception const &e) {
std::cout<<__LINE__<<" "<<e.what()<<std::endl;
}
}
};
int main() try {
// A a; // std::terminate is called, exception not caught at line 26. The try-catch block in main is not relevant.
B b;
return 0;
} catch (std::exception const &e) {
std::cout<<__LINE__<<" "<<e.what()<<std::endl;
}
Godbolt link: example
So class B's destructor catches the exception and prints: 16 ~B
Were in case of class A it calls terminates and prints:
terminate called after throwing an instance of 'std::runtime_error'
what(): ~A
7 ~A
May someone hint thy is this happening? (seems implicit rethrow at the end of function style try/catch, both on clang and GCC) Does standard somehow specifying this behaviour? Any quote/link may be very helpfull. Thanks in advace.
A function-level catch on a constructor or destructor automatically rethrows the current exception upon exit:
https://en.cppreference.com/w/cpp/language/function-try-block
Every catch-clause in the function-try-block for a constructor must terminate by throwing an exception. If the control reaches the end of such handler, the current exception is automatically rethrown as if by throw;. The return statement is not allowed in any catch clause of a constructor's function-try-block.
Reaching the end of a catch clause for a function-try-block on a destructor also automatically rethrows the current exception as if by throw;, but a return statement is allowed.
So, your noexcept on ~A() is lying to the compiler. If a noexcept function exits because of an uncaught exception, terminate() is called:
https://en.cppreference.com/w/cpp/language/noexcept_spec
Non-throwing functions are permitted to call potentially-throwing functions. Whenever an exception is thrown and the search for a handler encounters the outermost block of a non-throwing function, the function std::terminate is called
So I was reading about function try block in this link. And there was a line that describes the difference between normal try block and function try block like this
unlike normal catch blocks, which allow you to either resolve an exception, throw a new exception, or rethrow an existing exception, with function-level try blocks, you must throw or rethrow an exception
But then I try to write a function try block like this
#include <iostream>
int add(int a, int b) try {
throw 1;
return a + b;
}
catch (int) {
std::cout << "catch in add()";
}
int main()
{
try {
add(1, 2);
}
catch (int) {
std::cout << "catch in main()";
}
}
The output is
catch in add()
If function try block doesn't allow us to resolve an exception, then how come catch in main() doesn't got printed
And there was a line that describes the difference between normal try block and function try block like this
And that line is inaccurate. Function try blocks for regular functions, behave pretty much as if they were simply the only contents of the function. Meaning, your definition of add acts exactly the same as
int add(int a, int b) {
try {
throw 1;
return a + b;
}
catch (int) {
std::cout << "catch in add()";
}
}
The difference arises in constructors. For one, a function level try block is the only way to catch exceptions raised while initializing the members of the class.
And secondly, once an exception is thrown from a member's initialization, the constructor cannot complete, and so the object is not initialized. It is here that we are not allowed to simply swallow the exception. If initialization fails by throwing an exception, that exception must be propagated or translated to another type and rethrown.
The example on the page you linked summarizes that in code
B(int x) try : A(x) // note addition of try keyword here
{
}
catch (...) // note this is at same level of indentation as the function itself
{
// Exceptions from member initializer list or constructor body are caught here
std::cerr << "Exception caught\n";
// If an exception isn't explicitly thrown here, the current exception will be implicitly rethrown
}
Does function try block allows us to resolve a exception?
Yes.
The mandatory automatic throw only applies to a few cases such as constructors and destructors. The example function is neither a constructor nor a destructor.
P.S. The behaviour of the example is undefined because it fails to return a value from non-void function.
The tutorial you're following is partly mistaken. It says this:
Finally, unlike normal catch blocks, which allow you to either resolve an exception, throw a new exception, or rethrow an existing exception, with function-level try blocks, you must throw or rethrow an exception. If you do not explicitly throw a new exception, or rethrow the current exception (using the throw keyword by itself), the exception will be implicitly rethrown up the stack.
This is completely incorrect with regards to functions.
However, this is true with regards to constructors and destructors. The C++17 standard says this:
13 If a return statement appears in a handler of the function-try-block of a constructor, the program is ill-formed.
14 The currently handled exception is rethrown if control reaches the end of a handler of the function-try-block of a constructor or destructor. Otherwise, flowing off the end of the compound-statement of a handler of a function-try-block is equivalent to flowing off the end of the compound-statement of that function.
-- N4713 [except.handle] (emphasis mine)
The first sentence of point 14 confirms this behavior for constructors and destructors. The second sentence directly contradicts the information in the tutorial you are following, because they did not distinguish the two cases.
Note that your code causes undefined behavior because the function's catch block does not throw an exception nor does it return a value, and the function doesn't return void. You must return a value from the catch block to avoid UB.
From other threads, I know we should not throw exception in destructor! But for below example, it really works. Does that means we can only throw exception in one instance's destructor? how should we understand this code sample!
#include <iostream>
using namespace std;
class A {
public:
~A() {
try {
printf("exception in A start\n");
throw 30;
printf("exception in A end\n");
}catch(int e) {
printf("catch in A %d\n",e);
}
}
};
class B{
public:
~B() {
printf("exception in B start\n");
throw 20;
printf("exception in B end\n");
}
};
int main(void) {
try {
A a;
B b;
}catch(int e) {
printf("catch in main %d\n",e);
}
return 0;
}
The output is:
exception in B start
exception in A start
catch in A 30
catch in main 20
Best practice prior to C++17 says to not let exceptions propagate out of a destructor. It is fine if a destructor contains a throw expression or calls a function that might throw, as long as the exception thrown is caught and handled instead of escaping from the destructor. So your A::~A is fine.
In the case of B::~B, your program is fine in C++03 but not in C++11. The rule is that if you do let an exception propagate out of a destructor, and that destructor was for an automatic object that was being directly destroyed by stack unwinding, then std::terminate would be called. Since b is not being destroyed as part of stack unwinding, the exception thrown from B::~B will be caught. But in C++11, the B::~B destructor will be implicitly declared noexcept, and therefore, allowing an exception to propagate out of it will call std::terminate unconditionally.
To allow the exception to be caught in C++11, you would write
~B() noexcept(false) {
// ...
}
Still, there would be the issue that maybe B::~B is being called during stack unwinding---in that case, std::terminate would be called. Since, before C++17, there is no way to tell whether this is the case or not, the recommendation is to simply never allow exceptions to propagate out of destructors. Follow that rule and you'll be fine.
In C++17, it is possible to use std::uncaught_exceptions() to detect whether an object is being destroyed during stack unwinding. But you had better know what you're doing.
The recommendation that "we should not throw exception in destructor" is not an absolute. The issue is that when an exception is thrown the compiler starts unwinding the stack until it finds a handler for that exception. Unwinding the stack means calling destructors for objects that are going away because their stack frame is going away. And the thing that this recommendation is about occurs if one of those destructors throws an exception that isn't handled within the destructor itself. If that happens, the program calls std::terminate(), and some folks think that the risk of that happening is so severe that they have to write coding guidelines to prevent it.
In your code this isn't a problem. The destructor for B throws an exception; as a result, the destructor for a is also called. That destructor throws an exception, but handles the exception inside the destructor. So there's no problem.
If you change your code to remove the try ... catch block in the destructor of A, then the exception thrown in the destructor isn't handled within the destructor, so you end up with a call to std::terminate().
EDIT: as Brian points out in his answer, this rule changed in C++11: destructors are implicitly noexcept, so your code should call terminate when the the B object is destroyed. Marking the destructor as noexcept(false) "fixes" this.
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?
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!
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.
I have an exception class:
class MyException : public std::exception
{
public:
MyException( char* message )
: message_( message )
{
if( !message_ ) throw std::invalid_argument("message param must not be null");
}
};
And at my throw site:
try {
throw MyException( NULL );
}
catch( std::exception const& e ) {
std::cout << e.what();
}
(code was not compiled, so please excuse any errors)
I'm wondering what will happen when I throw from a constructor while constructing due to another throw. I assume this is legal, and the catch will end up catching a std::invalid_argument, and the previous exception thrown (MyException) will be ignored or cancelled out.
My goal with this design is to enforce invariants in my exception class. message_ should never be NULL, and I don't want to have if conditions to check if it is null in my what() overload, so I check them in the constructor and throw if they are invalid.
Is this correct, or is the behavior different?
The object you intend to throw (in this case MyException) must be successfully constructed before it can be thrown. So you're not throwing it yet, since it hasn't been constructed.
So this will work, throwing the exception from within MyException's constructor. You won't trigger the "throwing an exception while handling an exception causes std::terminate" issue.
15.1 Throwing an exception n3376
Paragraph 7
If the exception handling mechanism, after completing evaluation of the expression to be thrown but before the exception is caught, calls a function that exits via an exception, std::terminate is called (15.5.1).
This means that until the constructor (of the object being thrown in this case) completes nothing special is going to happen. But after the constructor completes any other uncought exception will result in terminate() being called.
The standard goes on to provide an example:
struct C
{
C() { }
C(const C&) { throw 0; }
};
int main()
{
try
{
throw C(); // calls std::terminate()
}
catch(C) { }
}
Here terminate is called because the object is first created. But then the copy construction is called to copy the exception to the holding location (15.1.4). During this function call (copy construction) an uncaught exception is generated and thus terminate is called.
So your code as shown should work as expected.
Either: A MyException is generated with a good message and thrown
Or: A std::invalid_argument is generated and thrown
If you want to check invariants that should always be true, you should use assertions. Exceptions are meant for unusual situations that are expected to happen, like corner cases or bad user input.
If you use exceptions to report bugs in your code, you could accidentally hide them with catch(...). This is especially important if you are writing library code, because then you never know if other people might catch and ignore the exception, even if it means that the system has reached an invalid state.
Another advantage of assertions is that you can completely disable them if you want, such that they will no longer incur any performance penalties. This allows you to be really paranoid with those checks in your debug builds and still have a lightning fast release build.