a exception from destructor, why terminate?

Dec 16, 2016 at 6:07am
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
#include <exception>
#include <iostream>
class A
{
public:
	~A(){
		throw std::exception("haha");
	}
};
int main()
{
	try
	{
		try
		{
			A a;
			throw std::exception("hehe");
		}
		catch(std::exception& e)
		{
			std::cout << e.what()<<std::endl;
		}
	}
	catch(std::exception& e)
	{
		std::cout << e.what()<<std::endl;
	}
	std::cin.get();
}


the aboving terminate reason is 'two active exception', but the following code also terminate, why? the first '~A()' throw an exception, why the second '~A()' continue?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#include <exception>
#include <iostream>
class A
{
public:
	~A(){
		throw std::exception("haha");
	}
};
int main()
{
	try
	{
		A a[2];
	}
	catch(std::exception& e)
	{
		std::cout << e.what()<<std::endl;
	}
	std::cin.get();
}




Last edited on Dec 16, 2016 at 6:32am
Dec 16, 2016 at 6:42am
what is causing you to test this?
Dec 16, 2016 at 8:25am
Because exceptions unwind the stack, and cause the destruction of the rest of the (automatic) objects in scope. Which throws a second exception.

Note: throwing exceptions from a destructor is a bad idea. A destructor can never fail to destroy *this.
Note: the outer try-catch block in your first snippet is redundant.
Dec 16, 2016 at 3:25pm
throw std::exception("haha");

what kind of implementation is that? Standard std::exception has no constructor taking a string

In any case, if you want to throw from a destructor (there are valid, but rare, reasons to do that), you must annotate it with noexcept(false), otherwise you get an unconditional terminate before anything else can happen

so it becomes:
1
2
3
	~A() noexcept(false) {
		throw std::runtime_error("haha");
	}

demo:
gcc: http://coliru.stacked-crooked.com/a/6028f8901c66d4c1 (doesn't terminate due to bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=66451 ).. and icc apparently has the same bug

clang: http://coliru.stacked-crooked.com/a/310f0ad60a9e4f7e (terminates as required by the spec: stack unwinding due to exception from the destructor of a[1] attempts to destroy a[0] which also throws)
Last edited on Dec 16, 2016 at 4:06pm
Dec 17, 2016 at 12:45am
@Cubbi, What I really asked is:
the first '~A()' throw an exception, why the second '~A()' continue?
Dec 17, 2016 at 3:41am
What do you mean by 'continue"? The second destructor (the destructor of a[0]) is called by the stack unwinding process initiated by the exception thrown from the first destructor (the destructor of a[1]). Because regardless of how you leave a scope, all objects local to that scope must have their destructors run.
Last edited on Dec 17, 2016 at 3:42am
Dec 24, 2016 at 2:51am
a[0] constructor
a[1] constructor
a[1] destructor //if throw here, a[0]'s destructor will go no?
a[0] destructor
Dec 24, 2016 at 4:28am
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
#include <stdexcept>
#include <iostream>
#include <typeinfo>

struct A
{
    ~A() noexcept(false)
    {
        std::cout << "destructor of object at " << this << std::flush ;

        if( std::uncaught_exception() )
        {
            std::cout << "\n     (non conforming compilers like the g++-6.2 / microsoft-2015 won't get here)\n" 
                      << "     destructor called during stack unwind, so should not throw - but throw anyway\n" << std::flush ;

            throw std::domain_error( "*** this is fatal, thrown during a stack unwind" ) ;  // throw anyway, if the compiler is not clang++
        }

        else
        {
            std::cout << " throws (there is no stack unwind in progress)\n" << std::flush ;
            throw std::domain_error( "ha ha (no stack unwind in progress)" ) ;
        }
    };
};

int main()
{
    std::set_terminate( [] { std::cout << "*** terminate is called ***\n" ; std::exit(1) ; } ) ;

	try
	{
		A a[2];
		std::cout << "a[0] at " << std::addressof(a[0]) << "  a[1] at " << std::addressof(a[1]) << '\n'
		          << "destructor of a[1] is called first, and it throws\n"
		          << "stack unwind should call the destructor of a[0], which too throws\n"
		          << "resulting in terminate being called\n-----------------------------------\n\n" << std::flush ;
	}

	catch( const std::exception& e )
	{
		std::cout << "caught " << typeid(e).name() << " - " << e.what() << '\n' ;
	}
}


clang++:
a[0] at 0x7fff18188c50  a[1] at 0x7fff18188c51
destructor of a[1] is called first, and it throws
stack unwind should call the destructor of a[0], which too throws
resulting in terminate being called
-----------------------------------

destructor of object at 0x7fff18188c51 throws (there is no stack unwind in progress)
destructor of object at 0x7fff18188c50
     (non conforming compilers like the g++-6.2 / microsoft-2015 won't get here)
     destructor called during stack unwind, so should not throw - but throw anyway
*** terminate is called ***

http://coliru.stacked-crooked.com/a/5b1c9b3c95e955bf

The other two compilers tested (g++-6.2 and microsoft-2015) are non-conforming; the GNU compiler does not even call terminate.

g++ (6.2):
a[0] at 0x7fffc28e6bf0  a[1] at 0x7fffc28e6bf1
destructor of a[1] is called first, and it throws
stack unwind should call the destructor of a[0], which too throws
resulting in terminate being called
-----------------------------------

destructor of object at 0x7fffc28e6bf1 throws (there is no stack unwind in progress)
caught St12domain_error - ha ha (no stack unwind in progress)


Microsoft (2015):
a[0] at 00EFFEF4  a[1] at 00EFFEF5
destructor of a[1] is called first, and it throws
stack unwind should call the destructor of a[0], which too throws
resulting in terminate being called
-----------------------------------

destructor of object at 00EFFEF5 throws (there is no stack unwind in progress)
destructor of object at 00EFFEF4 throws (there is no stack unwind in progress)
*** terminate is called ***

http://rextester.com/IHQO89137
Dec 27, 2016 at 6:09am
@JLBorges, Many thanks! You have helped me for several times.
Topic archived. No new replies allowed.