异常规范的评估点

Point of evaluation of exception specification

考虑这些代码片段:

版本 (1)

void q() {}
class B {
  void f() noexcept(noexcept(q())) {q(); }
  decltype(&B::f) f2;
};

版本 (2)

void q() {}
class B {
  void f() noexcept(true) {q(); }
  decltype(&B::f) f2;
};

版本 (3)

void q() {}
class B {
  void f() noexcept {q(); }
  decltype(&B::f) f2;
};

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

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

  decltype(&B::f) f2;

               ^

考虑到标准将 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;
};

此代码至少应在 C++ 14 之前有效,因为 noexcept 不是函数类型的一部分(对于 clang,它编译到版本 3.9.1)。对于 C++ 17,无法执行此操作。

这是 CWG 1330 的结果。

基本上,class 在其 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
};

我们需要知道 decltype(&B::f) 来处理 B::f2 的声明,但是为了知道那个类型,我们需要知道 noexcept-specifier[= B::f 的 31=] 是(因为现在它是类型系统的一部分),但是为了做到 that,我们需要评估 noexcept-完整 B.

上下文中的说明符

程序格式错误,clang 正确。

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

让我引用 Richard Smith 来论证为什么这是一个缺陷,以及为什么所有的例子都是错误的,因为异常规范只在 class.[=16= 的末尾被解析。 ]

Clang is approximately correct to reject that code.

Per C++ DR1330, exception specifications are not parsed until we reach the end of the class (they can name class members that are declared later, and the class is complete within them), just like member function bodies, default member initializers, and default arguments.

Finally, there's C++ DR361 (still open 16 years after being filed, sadly), wherein the conclusion of CWG is that the program is ill-formed if you use a delay-parsed construct before the end of the class. But we don't have actual standards wording to back that up yet, just a standard that's unimplementable without a fix to that defect.

LLVM Bug 37559, but also see Google Groups, CWG 361, CWG 1330

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

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