noexcept是否适用于从初始化列表传播的异常

时间:2016-08-07 20:59:49

标签: c++ c++11 constructor c++14 noexcept

假设我有一个像这样的构造函数

Something(SomethingElse) noexcept : member{SomethingElse, something_that_might_throw()} {
    ...
}

如果noexcept的构造可以抛出,member是否正常?在上面的示例中,成员变量member属于我不知道的类型。

旁注:使用noexcept时是否还需要担心其他边缘情况?

3 个答案:

答案 0 :(得分:9)

#UPDATE:(基于您的编辑):原始答案适用于函数的块范围中的所有内容(包含构造函数,包括构造函数) -initialization列表)。为什么不让你try代码看看。 : - )

#Original Answer

Something(SomethingElse) : member{SomethingElse} noexcept {
    ...
}

您的代码不会以这种方式编译。 noexcept位于冒号:

之前
Something(SomethingElse) noexcept : member{SomethingElse} {
    ...
}

问题:

  

如果member类构造函数可以,则在这种情况下noexcept是正常的   扔?

不,它是not okaynoexcept说明符是一个异常不会离开该函数或构造函数的承诺。如果它离开,运行时将终止你的程序。

  

如果搜索匹配的异常处理程序,则会标记一个函数   立即调用noexceptnoexcept(true)std::terminate

这适用于构造函数。

尽管try..catch阻止,但下面的代码会被终止:

struct Base {
    Base(){}
    Base(int){ throw int(5); }
};

struct Derived : public Base {
    Derived(int a) noexcept : Base(a) {}
};

int main()
{
    try{
        Derived d(45);
    } catch(...) {}
    return 0;
}

输出:

terminate called after throwing an instance of 'int'
bash: line 7:  7454 Aborted                 (core dumped) ./a.out

Live on Coliru

但是,如果删除noexcept规范,则不会突然终止程序,异常处理将继续正常进行。 :-)。

如果您在生产中执行此类操作,或者甚至是包含许多贡献者的大型代码库,我会让您想到后果。如果您不确定函数/构造函数块中所有语句的异常保证,请不要使用noexcept

答案 1 :(得分:3)

是的,构造函数上的noexcept适用于基类/成员构造。

如果你需要处理这种情况,你可能想要使用鲜为人知(并且很少使用)的function try block。语法如下所示:

#include <iostream>

class bad_initializer {};

int do_throw() { 
    throw bad_initializer();
    return 1;
}

class something { 
    int member;
public:
    something() noexcept
        try : member{do_throw()} // <-- initializer list
        {
            // ctor body goes here
        }
        catch(bad_initializer const &) { 
            std::cerr << "initialization threw\n";
        }
};

int main() { 
    something s;
}

现在,坏消息:因为你有一个没有构建的成员,catch块实际上只有几个选项。正常处理不能继续 - 构造对象时发生异常,这意味着对象无法完成构造。当你发现异常时,你无能为力。

如果它不是noexcept,它可以捕获异常,然后进行一些处理(释放它成功获取的任何资源)然后重新抛出它捕获的异常,或者抛出一个不同的异常(一个这更好地反映了它无法建造)。

在这种情况下,您有意义的选择会受到更多限制:您可以直接调用terminate,也可以抛出异常,间接调用terminate。关于在这种情况下完成的所有try / catch事件,可以让您有机会在调用terminate之前进行一些处理。

答案 2 :(得分:1)

一般情况下不行,但在某些情况下可能

noexcept是一个promise,类似于成员函数的const限定符,但与const不同,它不会在编译时强制执行(编译器不会发出错误)如果noexcept函数可能实际throw,则可能甚至没有警告,但请参见下文)。因此,你可以声明一个函数noexcept,即使编译器生成的检查会发现这个函数可能会抛出并且代码将被编译(否则你的问题将是不可能的)。

然而,这违背了noexcept的目的。主要目的是向程序员指出(1)他们可以使用某种功能而不必担心异常安全,并且(2)向编译器表明它不需要为堆栈展开添加代码。相反,如果在noexcept函数中抛出异常,则运行时将根据标准的要求调用std::terminate()

所以,一般来说,你的构造函数应该只有noexcept,如果它的所有功能,包括基础和成员的构造也是如此。

然而,有一个例外。如果你知道某些方法永远不会抛出任何输入在特定情况下,即使它可能在其他情况下抛出而因此不是noexcept,你可以在一个方法中调用此方法noexcept情况。例如,

// requires non-negative x, will throw std::runtime_error for negative x
int my_func(std::int64_t x);

struct my_struct {
  std::int64_t q;
  my_struct(int x) noexcept : q(std::int64_t(x)*std::int64_t(x)) {}
  int member() const noexcept { return my_func(q); }  // fine: q>=0
};

在您发布的情况下,这意味着如果member(somethingelse_type)不是noexcept,但您知道(根据您帖子中截断的代码之外的条件),不会针对特定参数抛出任何异常在这种情况下,您可以声明Something(SomethingElse) noexcept