异常规范的评估点

ove*_*eas 9 c++ language-lawyer noexcept c++17

请考虑以下代码段:

版本(1)

void q() {}
class B {
  void f() noexcept(noexcept(q())) {q(); }
  decltype(&B::f) f2;
};
Run Code Online (Sandbox Code Playgroud)

版本(2)

void q() {}
class B {
  void f() noexcept(true) {q(); }
  decltype(&B::f) f2;
};
Run Code Online (Sandbox Code Playgroud)

版本(3)

void q() {}
class B {
  void f() noexcept {q(); }
  decltype(&B::f) f2;
};
Run Code Online (Sandbox Code Playgroud)

所有版本的GCC都会编译这些代码片段而不会出现任何错误或警告(包括trunk-version).支持C++ 17的所有版本的Clang拒绝版本(1)和(2),但不支持版本(3),并出现以下错误:

<source>:4:16: error: exception specification is not available until end of class definition

  decltype(&B::f) f2;

               ^
Run Code Online (Sandbox Code Playgroud)

考虑到标准定义noexcept为等同于noexcept(true)[except.spec].因此,版本(2)和版本(3)应该是等价的,它们不适用于clang.

因此,以下问题:在什么时候需要根据C++ 17标准评估异常规范?并且,如果上面的某些代码无效,背后的理性是什么?


感兴趣的人的抽象背景:

template <typename F>
struct result_type;

template<typename R, typename C, typename... Args>
struct result_type<R(C::*)(Args...)> {
  using type = R;
}; // there may be other specializations ...

class B {
  int f() noexcept(false) { return 3; }
  typename result_type<decltype(&B::f)>::type a;
};
Run Code Online (Sandbox Code Playgroud)

此代码至少应该在C++ 14之前有效,因为noexcept它不是函数类型的一部分(对于clang,它编译为版本3.9.1).对于C++ 17,没有办法做到这一点.

Bar*_*rry 5

这是CWG 1330的结果.

基本上,该类在其noexcept-specifier中被认为是完整的(在上面的缺陷的解决方案中称为异常规范).

这使我们处于以下情况:

void q() {}
class B {
  void f() noexcept(noexcept(q())) {q(); }
//                  ~~~~~~~~~~~~~
//                  evaluated in the context of complete B

  decltype(&B::f) f2;
//~~~~~~~~~~~~~~~
//cannot wait until B is complete to evaluate
};
Run Code Online (Sandbox Code Playgroud)

我们需要知道decltype(&B::f)要处理的声明B::f2,但为了知道类型,我们需要知道的是什么noexcept说明符B::f是(因为现在是这样的类型系统的一部分),但是为了做,我们需要在完整的上下文中评估noexcept-specifierB.

该计划格式不正确,铿锵是正确的.


ove*_*eas 0

我将参考所有相关资源亲自回答这个问题。

让我引用 Richard Smith 来论证为什么这是一个缺陷,以及为什么所有示例都是格式错误的,因为异常规范仅在类末尾进行解析。

Clang 拒绝该代码大致是正确的。

根据 C++ DR1330,异常规范在我们到达类末尾之前不会被解析(它们可以命名稍后声明的类成员,并且类在其中完成),就像成员函数体、默认成员初始值设定项和默认参数一样。

最后,还有 C++ DR361(遗憾的是,在提交 16 年后仍然开放),其中 CWG 的结论是,如果您在课程结束之前使用延迟解析构造,则程序的格式不正确。但我们还没有实际的标准措辞来支持这一点,只是一个如果不修复该缺陷就无法实现的标准。

LLVM Bug 37559,还请参阅Google GroupsCWG 361CWG 1330

此外,noexcept原则上应该完全等价noexcept(true)于 [ except.spec] 中所述,并且接受noexcept但拒绝显然是错误的noexcept(true)

因此,结论是所有示例都是格式错误的,即使这不是所需的行为。