【问题标题】:Point of evaluation of exception specification异常规范的评估点
【发布时间】:2018-10-30 05:52:42
【问题描述】:

考虑一下这些代码 sn-ps:

版本(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 编译这些代码 sn-ps 没有任何错误或警告(包括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;

               ^

考虑到标准将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,没有办法这样做。

【问题讨论】:

  • 您是否尝试过完全限定q:noexcept(noexcept(::q()))
  • 这没有影响。你甚至可以写noexcept(true)。即,只要 noexcept 包含表达式并且不仅仅是 noexcept,clang 就拒绝编译。如果这符合标准,我会感到惊讶,但我找不到来源。

标签: c++ language-lawyer c++17 noexcept


【解决方案1】:

这是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
};

我们需要知道decltype(&amp;B::f) 来处理B::f2 的声明,但是为了知道那个类型,我们需要知道B::fnoexcept-specifier 是什么(因为现在这是类型系统的一部分),但为了做到,我们需要在完整的B 的上下文中评估noexcept-specifier

程序格式不正确,clang 是正确的。

【讨论】:

  • 谢谢!我添加了一个额外的示例,您能否对此发表评论?
  • @overseas noexcept(noexcept(q()))noexcept(true) 之间并没有真正的区别——关键在于它是 noexcept(something)。只需noexcept 即可,不涉及任何表达式解析。
  • 但是第 15.4 节 [except.spec] 说:“一个 noexcept 规范 noexcept 等效于 noexcept(true)”。此外,“noexcept-specification”被定义为noexcept noexcept(constant-expression)。这就是为什么我对在哪个具体部分找到这个感到困惑。我将-重新添加已解决的-一旦解决了这种混乱就标记:-)
  • 在不知道上下文的情况下,实现解析任意表达式是不可能的。我想你可以特例 noexcept(true)noexcept(false),但这更加随意。 (这几乎可以肯定在标准中没有明确说明。请注意,GCC 根本没有实现 1330。)
  • @T.C.是的,我同意这是任意的。但如果noexcept(true/false)-case 无效,但noexcept-case 有效,则肯定是错误的,因为后者显然是由前者定义的。因此,似乎有三个可能的答案:1)所有版本都是无效的。 2)所有版本都无效,除了noexcept(true/false),这在某种程度上是任意的。 3)所有版本都是有效的,我们已经用@Barry 回答排除了这些版本。可能它没有指定,但我仍然会对在这种情况下是否可以依赖任何行为感兴趣。
【解决方案2】:

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

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

Clang 拒绝该代码大致正确。

根据 C++ DR1330,直到我们到达类的末尾才解析异常规范(它们可以命名稍后声明的类成员,并且类在其中是完整的),就像成员函数体、默认成员初始化器、和默认参数。

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

LLVM Bug 37559,也请参见Google GroupsCWG 361CWG 1330

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2013-09-15
    • 1970-01-01
    • 1970-01-01
    • 2023-04-08
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多