请考虑以下代码段:
版本(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都会编译这些代码片段而不会出现任何错误或警告(包括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,无法执行此操作。
答案 0 :(得分:5)
这是CWG 1330的结果。
基本上,该类在其 noexcept-specifier 中被认为是完整的(在上面的缺陷的解析中称为 exception-specification )。 / p>
这使我们处于以下情况:
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
的声明,但为了知道该类型,我们需要知道{em> noexcept-specifier 的内容{ {1}}是(因为现在它是类型系统的一部分),但是为了那个,我们需要在上下文中评估 noexcept-specifier 。完成B::f
。
该程序格式不正确,铿锵是正确的。
答案 1 :(得分:0)
我将自己参考所有相关资源来回答这个问题。
让我引用Richard Smith来说明为什么这是一个缺陷,以及为什么所有示例都格式错误,因为异常说明仅在类的末尾进行解析。
使用Clang拒绝该代码大致正确。
对于每个C ++ DR1330,直到我们到达类的末尾(它们可以命名稍后声明的类成员,并且该类在其中完成),才会解析异常规范,就像成员函数体,默认成员初始化器,和默认参数。
最后,还有C ++ DR361(可悲的是,在提交后仍然开放16年),其中CWG的结论是,如果您在课程结束之前使用延迟分析的构造,则该程序格式不正确。但是我们还没有实际的标准措辞来支持这一点,只是没有解决该缺陷就无法实现的标准。
LLVM Bug 37559,但另请参见Google Groups,CWG 361,CWG 1330
此外,如[except.spec]中所述,noexcept
原则上应完全等同于noexcept(true)
,并且接受noexcept
显然是错误的,但拒绝{{1 }}。
因此,结论是所有示例都是错误的,即使这不是理想的行为。