是否复制构造函数和std :: runtime_error的复制赋值noexcept?

时间:2016-01-27 14:23:29

标签: c++ exception-handling c++14

所有GCC 4.8.4,4.9.3,5.3.0都通过std::exception的测试(对于任何-std = c ++ 11 / 1y / 14 / 1z / 17选项,如果可用) :

static_assert(std::is_nothrow_copy_constructible<std::exception>::value, "test exception");
static_assert(std::is_nothrow_copy_assignable   <std::exception>::value, "test exception");

哪个没问题,因为std::exception没有特殊成员(C ++ 14 18.8.1):

namespace std {
  class exception {
  public:
    exception() noexcept;
    exception(const exception&) noexcept;
    exception& operator=(const exception&) noexcept;
    virtual ~exception();
    virtual const char* what() const noexcept;
  };
}

不幸的是,上面的所有编译器都失败了以下static_assert

static_assert(std::is_nothrow_copy_constructible<std::runtime_error>::value, "test runtime_error");
static_assert(std::is_nothrow_copy_assignable   <std::runtime_error>::value, "test runtime_error");

该标准仅包含以下关于19.2.6中的std::runtime_error

namespace std {
  class runtime_error : public exception {
  public:
    explicit runtime_error(const string& what_arg);
    explicit runtime_error(const char* what_arg);
  };
}

但是对于noexcept其他(隐式声明的特殊)成员以及what_arg的存储实现要求,没有任何说法。

标准(C ++ 14)在15.4 / 14中说明如下:

  

继承构造函数(12.9)和隐式声明的特殊   成员函数(第12条)具有异常规范。如果f是   继承构造函数或隐式声明的默认值   构造函数,复制构造函数,移动构造函数,析构函数,副本   赋值运算符,或移动赋值运算符,其隐式   exception-specification指定type-id T当且仅当T为时   直接调用的函数的异常规范允许   通过f的隐含定义;如果有任何功能,f允许所有异常   直接调用允许所有异常,而f具有   异常规范noexcept(true)如果每个函数都是直接的   invokes不允许例外。

以下是18.8.1 / 2:

  

从类异常派生的每个标准库类T都应该   有一个可公开访问的复制构造函数和一个公开的   无法退出的可访问复制赋值运算符   异常。

由于std::runtime_error没有公开what_arg存储的实现,我们不知道它的(特殊)成员是否是noexcept,因此std::runtime_error副本的noexceptness构造函数或复制赋值成员是不可判定的。我们唯一的赌注是18.8.1以上。

问题1 / a)我们认为std::runtime_error的复制构造函数或复制赋值为noexcept(12)。这是真实的/最先进的/最佳实践吗?

问题1 / b)我们不需要在标准中明确说明这一点吗? (如18.8.2,班级bad_exception

问题1 / c)GCC中的错误是上面的static_assert测试失败了吗?

问题2)如果上面的推论是错误的,有人可以指向标准中的段,其中说明std :: runtime_error没有复制构造函数(和复制赋值)吗? (或者说它们不是。)

1 个答案:

答案 0 :(得分:2)

考虑LWG 1371

  

第19节中定义的任何异常类型都不允许在复制或移动操作上抛出异常,但没有明确规定操作具有异常规范来证明它。请注意,如果所有数据成员都类似地声明std::exception操作,那么从其基类(最终noexcept)获取异常规范的隐式声明的构造函数将隐式生成noexcept异常规范。由于表示未指定,我们不能假设非抛出操作,除非我们明确地将其声明为实现的约束。

     

[投票评论提出的决议:]

     

添加第19条中定义的所有异常类型的全局保证   依赖于隐式声明的操作具有非投掷性   关于这些操作的异常规范。

在2010年巴达维亚会议上,发现[exception]/2“覆盖了这个”:

  

派生自类exception的每个标准库类T都应具有可公开访问的复制构造函数和不能以异常退出的可公开访问的复制赋值运算符。

因此,仍然没有规定这些特殊成员函数是noexcept。并且根据[except.spec]/16中确定隐式异常规范的方式,由于实现可以使用默认参数和成员添加任意参数,因此无论这些特殊成员函数是否为noexcept,它都是特定于实现的。