Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Should exceptions thrown from class member initializers call std::terminate()?

Given this code:

struct A {
    A(int e) { throw e; }
};

struct B {
    A a{42}; // Same with = 42; syntax
};

int main() {
    try {
        B b;
    } catch (int const e) {
        return e;
    }
}

When compiled with GCC (versions 4.7.4, 4.8.5, 4.9.3, 5.4.0, 6.3.0):

$ g++ -std=c++11 test.cpp -o test; ./test ; echo $?
terminate called after throwing an instance of 'int'
Aborted
134

But when compiled with Clang (version 4.0.0):

$ clang++ -std=c++11 test.cpp -o test; ./test ; echo $?
42

Which behavior is correct?

like image 799
jotik Avatar asked May 10 '17 12:05

jotik


2 Answers

This is a bug in GCC (Bug 80683).

If the constructor is the first op in the try/catch clause, then the compiler considered it as being outside of it although it should include it.

For example, the following works just fine:

#include <iostream>

struct A {
    A(int e) { throw e; }
};

struct B {
    A a{42}; // Same with = 42; syntax
};

int main() {
    try {
        // The following forces the compiler to put B's contructor inside the try/catch.
        std::cout << "Welcome" << std::endl; 
        B b;
    } catch (int e) {
        std::cout << "ERROR: " << e << std::endl; // This is just for debugging
    }

    return 0;
}

Running:

g++ -std=c++11 test.cpp -DNDEBUG -o test; ./test ; echo $?

Outputs:

Welcome
ERROR: 42
0

My guess is that is that due to compiler optimization, it moves the constructor to the beginning of the main function. It assumes that the struct B has no constructor, then it assumes that it would never throw an exception, thus it is safe to move it outside of the try/catch clause.

If we will change the declaration of struct B to explicitly use struct A constructor:

struct B {
    B():a(42) {}
    A a;
};

Then the result will be as expected and we will enter the try/catch, even when removing the "Welcome" printout:

ERROR: 42
0
like image 181
Liran Funaro Avatar answered Oct 14 '22 15:10

Liran Funaro


Clang is correct. References about that can be found in C++ standard reference draft n4296 (emphasize mine):

15.3 Handling an exception [except.handle]

...
3 A handler is a match for an exception object of type E if
(3.1) — The handler is of type cv T or cv T& and E and T are the same type (ignoring the top-level cv-qualifiers),

Here an int is thrown, and the handler declares a const int. There is a match and the handler shall be invoked.

like image 36
Serge Ballesta Avatar answered Oct 14 '22 13:10

Serge Ballesta