隐式生成的成员和 noexcept

Posted

技术标签:

【中文标题】隐式生成的成员和 noexcept【英文标题】:Implicit generated members and noexcept 【发布时间】:2012-02-07 16:41:15 【问题描述】:

我最近开始添加新的noexcept 规范以尽可能移动构造函数/赋值。现在我开始想知道隐式生成的成员函数的异常规范是什么样的。由于拥有noexcept 移动函数允许使用更有效的代码路径(例如,在调整vector 的大小时)我希望尽可能将它们声明为noexcept。我无法理解标准对此的规定,因此尝试了 g++4.6 中的以下代码(使用-std=c++0x)来掌握它:

struct foobar;
int main()

    foobar a, b;
    std::cout<<std::boolalpha
             <<noexcept(foobar())<<", "<<noexcept(foobar(a))<<", "
             <<noexcept(a = b)   <<", "<<noexcept(a = std::move(b))<<", "
             <<noexcept(foobar(std::move(a)))<<std::endl;

这给了我True, True, True, False, False 的输出,这意味着noexcept 的默认和复制构造函数/赋值,而不是移动操作。

现在回答我的问题:

在什么情况下隐式生成(或默认)的成员函数声明为noexcept?此外,foobar 观察到的行为是正确的还是只是 gcc4.6 中的编译器错误?

【问题讨论】:

【参考方案1】:

库错误 — 它在 gcc 4.7 中显示 true, true, true, true, true

错误是不是生成的移动构造函数不是 noexcept,而是 std::move 没有标记为 noexcept,正如我们在附加测试中看到的那样:

std::cout << noexcept(a = static_cast<foobar&&>(b)) << ", "  // true
          << noexcept(foobar(static_cast<foobar&&>(b))) << ", " // true
          << noexcept(std::move(b)) << std::endl;   // false

gcc 4.6 中的大多数库函数都不是 noexcept 正确的,这已在 gcc 4.7 中得到解决,


至于隐含生成的成员函数何时为 noexcept,这在 §15.4/14 中有记录。基本上,如果它需要调用的所有函数都是noexcept,它就是noexcept

隐式声明的特殊成员函数(第 12 条)应具有异常规范。如果f 是 隐式声明的默认构造函数、复制构造函数、移动构造函数、析构函数、复制赋值 运算符或移动赋值运算符,其隐含的 exception-specification 指定 type-id T 当且仅 如果Tf 的隐式定义直接调用的函数的异常规范 允许; f应 如果它直接调用的任何函数允许所有异常,则允许所有异常,f 不应允许任何异常 如果它直接调用的每个函数都不允许出现异常。

【讨论】:

那么这是否意味着这些函数尽可能是noexcept(也就是它们不调用任何抛出函数)? @Grizzly:我认为这意味着如果他们只调用noexcept 函数,他们将是noexcept(即是否调用throw 的实际函数无关紧要,异常规范才是重要的)

以上是关于隐式生成的成员和 noexcept的主要内容,如果未能解决你的问题,请参考以下文章

C++ 隐式生成的赋值运算符的异常安全性

区别 b/w 在 C# 中隐式实现成员和显式实现成员 [重复]

C++ 类中由具有重载隐式转换的空结构成员计算的成员

为啥 PySide 会从 Signals 的类成员中隐式创建对象成员?

隐式移动构造函数和赋值运算符

为啥不能隐式实现非公共接口成员?