从成员构造函数中抛出异常(花括号初始化器 vs 初始化列表)

9

我对于在成员对象初始化期间(从构造函数中)抛出异常的情况失去了信心(也可能浪费了2个小时)。

让我给你举个例子:

int init (int f) {
    throw f;
}

struct X {
    X (int f) : n {init (f)} {}
    int n;
};

struct P {
    X x {20};
};

以及使用方法:

int main (int argc, char** argv) {
    try {
        P p {};
    }
    catch (int n) {
        std::cout << n << "\n";
    }
}

这段代码(C++11模式)在GCC 7.2.1中可以编译通过,在Linux(Centos 7.4.1708)下运行时会出现以下结果:
terminate called after throwing an instance of 'int'
[1]    1242 abort (core dumped)  ./main

问题是:为什么?为什么 catch()段没有被执行? 我已经追踪了这个问题,意味着我的P类有点不同:
struct P {
    P (int f) : x {f} {}
    X x;
};

并且使用以下形式初始化 p 对象: P p {20},结果是: 20,这是预期的,没有核心转储
在异常上下文中,有谁能解释一下使用 花括号或等号初始化程序成员初始化器列表 初始化成员之间的区别吗?
缺陷报告: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=85363

3
有趣的是,使用 -std=c++11 编译您的代码会产生您提到的结果,但使用 c++14c++17 一切看起来都很好。 - JBL
6
对我来说,这似乎是一个编译器的错误。考虑到JBL发现的情况,以及在clang++的c++11模式下代码可以正常工作。而且我认为程序本身没有任何问题。 - eerorika
没错,在coliru上使用clang和c++11可以正常工作。 - JBL
3
我可以用VS2015重现这个问题。添加P() {}可以捕获异常,但是 P() = default; 没有帮助。std::is_nothrow_default_constructible<P>::value; 似乎为 true,这是一个问题。似乎在决定默认构造函数是否 noexcept 时,默认成员初始值器不被考虑在内。 - François Andrieux
@FrançoisAndrieux 这很奇怪。在 g++ 中 std::is_nothrow_default_constructible<P>::value;false,即使它没有捕获到异常。 - eerorika
我可以使用g++ -std=c++11进行复现,但是无法在Visual C++ 2017(对于任何值的/std:)中进行复现。请报告此错误。 - Cheers and hth. - Alf
1个回答

1

以下两种方式在实际上没有区别:

struct P {
    X x {20};
};

并且

struct P {
    P() : x{20} { }
    X x;
};

但是在C++11模式下有一个GCC bug(但不在C++14或更高版本中),它会导致p {}的初始化发生在try块外,或者为P的隐式默认构造函数添加等效的noexcept

自从 OP 报告了这个错误,我以为它已经在评论中链接了,但显然没有。 - Jonathan Wakely

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接