【问题标题】:Does noexcept apply to exceptions propagating from initializer listsnoexcept 是否适用于从初始化列表传播的异常
【发布时间】:2016-12-13 14:19:23
【问题描述】:

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

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

如果member 的构造可以抛出,那么在这种情况下noexcept 可以吗?在上面的例子中,成员变量member 属于我不知道的类型。

附带说明:在使用 noexcept 时是否需要担心其他边缘情况?

【问题讨论】:

  • @personwhodownvotedme 这个问题有什么问题?
  • 不认识的人不要。有显示名为downvoter的用户。
  • @VeniVidiVici 如果用户在问题中没有任何活动,我认为不会向用户发送通知?
  • @VeniVidiVici 有什么区别?
  • 似乎是一个很好的理由(对某人而言)投反对票……(不过我投了赞成票)。

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


【解决方案1】:

#UPDATE:(基于您的编辑):原始答案适用于函数 block-scope 内的所有内容(包括构造函数,包括 构造函数) -初始化列表)。你为什么不try代码看看。 :-)


#原始答案

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

您的代码不会以这种方式编译。 noexcept 出现在冒号之前 :

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

你的问题:

如果member 类构造函数可以 扔?

不,是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


【讨论】:

    【解决方案2】:

    是的,构造函数上的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 被调用之前进行一些处理。

    【讨论】:

    • 哦,我不知道它存在!
    • 但您似乎错过了something() 构造函数的定义,对吧?
    • @Curious:不——但我添加了一些 cmets 来澄清。
    • 嗯,有道理。谢谢!
    【解决方案3】:

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

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

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

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

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

    // 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

    【讨论】:

    • 这是不正确的。规范要求如果标记为noexcept 的函数抛出异常,程序会立即通过调用std::terminate 终止。这不仅仅是一个评论,或者一个“未强制执行的承诺”。请参阅 c++11 标准中的第 15.4 节,尤其是 15.4.10。我看到您后来尝试将其包含在内并将其与您的答案相协调,但您的第一段具有误导性,我不得不投反对票。
    • @ChrisBeck 我理解“某些方法永远不会在特定情况下抛出任何可能的输入,即使它可能在其他情况下抛出”意味着该函数理论上可以抛出但没有可能的输入实际上给予它的将是一个抛出。在这种情况下,程序员知道它实际上是 noexcept 毕竟。我误会了吗?
    • @ChrisBeck 你在抱怨什么?我确实说过(在我的第二段结尾)来自noexcept 函数的throw 将调用terminate()。那么什么是不正确的呢?请更准确。
    • @Walter:好吧,你现在改变了答案,但引发咆哮的是关于noexcept 的部分是一个未强制执行的承诺。我不怀疑你理解它,但我认为这不是解释它的好方法。
    猜你喜欢
    • 1970-01-01
    • 2021-12-06
    • 2018-06-24
    • 1970-01-01
    • 2012-03-29
    • 2020-10-17
    • 1970-01-01
    • 1970-01-01
    • 2020-03-29
    相关资源
    最近更新 更多