什么noexcept完全包含在构造函数中?

时间:2016-03-29 21:49:57

标签: c++ c++11 constructor language-lawyer noexcept

根据C ++标准,类构造函数上的noexcept noexcept-specification 究竟适用于什么?

  1. 功能体
    1. 在可选的 ctor-initializer 中初始化成员?
      1. 在可选的 mem-initializer 中初始化基类?
      2. 在可选的 mem-initializer 中初始化类成员?
    2. 复合语句
    3. function-try-block
  2. 初始化未在 ctor-initializer 中初始化的对象基类?
  3. 初始化 ctor-initializer 中未初始化的对象类成员?
  4. 额外的东西?
  5. 换句话说,noexcept noexcept-specification 包含上述哪一项(即std::terminate()时抛出异常时触发noexcept(true)

    请提供该标准的参考。关于使用noexcept构造函数的任何警告的提示也是受欢迎的。谢谢!

2 个答案:

答案 0 :(得分:5)

  

换句话说,noexcept noexcept-specification 包含了以上哪些内容?

异常规范(noexcept和动态异常规范)涉及基类的构造,成员的构造和初始化,以及构造函数体中的代码。 基本上,在构造对象时执行的所有函数 - 这是有道理的,因为异常规范与对象的构造函数相关联,因此它应该涵盖在构造对象期间执行的代码;如果建筑的任何部分不在此范围内,那将是违反直觉的。

支持标准报价......

如果在施工期间抛出异常(并且可能未处理)该怎么办?

[except.spec]/9

  

每当抛出类型E的异常并且搜索处理程序([except.handle])遇到函数的最外面的块时,异常规范不允许E,那么,

     
      
  • 如果函数定义具有动态异常规范,则调用函数std::unexpected()([except.unexpected]),
  •   
  • 否则,调用函数std::terminate()([except.terminate])。
  •   

“函数的最外面的块”是什么意思?函数的主体。 1

上面的exception specification包含 noexcept-specification

如何在隐式声明的构造函数上确定隐式声明的异常规范?

[except.spec]/15

  

某些类f的隐式声明的特殊成员函数X被认为具有隐式异常规范,该规范由以下集合中的所有成员组成:

     
      
  • 如果f是构造函数,

         
        
    • 构造函数调用的潜在异常集

           
          
      • 表示X的非变体非静态数据成员
      •   
      • 表示X的直接基类,
      •   
      • 如果X是非抽象的([class.abstract]),对于X的虚拟基类,

             

        (包括在此类调用中使用的默认参数表达式),由f([class.ctor])的隐式定义的重载决策选择...

      •   
    •   
    • brace-or-equal-initializers 中非静态数据成员初始化的潜在异常集(未被忽略)([class.base.init]); < / p>

    •   
  •   

这提供了非常有用的说明,说明编译器将使用什么来确定(并因此考虑覆盖)异常规范。

1 “函数的最外层”是什么意思?对函数块定义的关注有一个评论。该标准没有函数块的正式定义。短语功能块仅用于Exception Handling [except]。从C ++ 98开始,这个短语被包含在标准中。

为了进一步明确这一点,我们需要寻找其他来源并得出一些合理的结论。

来自Stroustrup C++ glossary;

  

函数体 - 函数的最外层块。另请参见:try-block,函数定义。 TC ++ PL 2.7,13。

[dcl.fct.def.general]/1 function-body 的语法,用复合语句覆盖 ctor-initializer 功能试块;

  

函数定义具有以下形式;

     

...

     

功能体
   ctor-initializer opt 复合语句
  功能试块

     

...

     

对函数体的任何非正式引用都应解释为对非终端函数体的引用...

同样重要的是要记住,异常规范与函数相关联,而不是一般代码块(作用域等)。

鉴于异常处理子句和Stroustrup FAQ中短语的年龄,函数的 function-body 相同,标准可以可能会更新异常子句中使用的语言。

根据以下代码,对a1a2a3(当其他人被注释掉时)的构建的一些经验证据导致std::terminate调用。结果适用于g++, clangMSVC

struct Thrower { Thrower() { std::cout << "throwing..." << std::endl; throw 42; } };

struct AsMember { Thrower t_; AsMember() noexcept : t_{} { std::cout << "ctor" << std::endl; } };

struct AsBase : Thrower { AsBase() noexcept { std::cout << "ctor" << std::endl; } };

struct AsNSDMI { Thrower t_ {}; AsNSDMI() noexcept { std::cout << "ctor" << std::endl; } };

int main()
{
    std::set_terminate([](){ std::cout << "terminating..." << std::endl; });
    try {
        //AsMember a1{};
        //AsBase a2{};
        AsNSDMI a3{};
    }
    catch (...) { std::cout << "caught..." << std::endl; }
    return 0;
}

答案 1 :(得分:3)

如果您考虑当前翻译单元中没有定义的noexcept构造函数(例如,构造函数的实现位于共享库中),您可以自己回答这个问题。

如果noexcept不适用于整个构造函数,构造函数仍然会以某种方式发出异常,从而无法实现noexcept的目的。

noexcept在这种情况下正常工作的唯一方法是它是否适用于整个构造函数,包括基类和成员初始化。