noexcept函数还能调用c++ 17中抛出的函数吗?
Can a noexcept function still call a function that throws in C++17?
在P0012R1中,"使异常规范成为类型系统的一部分",
我看到noexcept
现在变成了函数类型的一部分。
我不知道这是否会阻止noexcept(true)
函数仍然能够调用noexcept(false)
函数。
下面的代码在c++ 17中仍然有效吗?
void will_throw() noexcept(false){
throw 0;
}
void will_not_throw() noexcept(true){
will_throw();
}
根据cppreference:
注意,函数的
noexcept
规范不是编译时的检查;它只是程序员通知编译器的一种方法函数是否应该抛出异常。
所以你的代码的语法是有效的,但是std::terminate
将在执行时被调用
不幸的是,它在编译时是有效的。
虽然noexcept被编译器用来优化异常处理代码,使代码更高效,但遗憾的是他们没有进一步给noexcept一个语义意义。
理想情况下,当您将方法标记为noexcept时,还应该意味着该方法不应该让任何异常冒泡。因此,如果你有一个标记为noexcept的方法,但它调用了其他未标记为noexcept的方法,那应该会给你一个编译错误,除非有一个try/catch块围绕着任何可能抛出的东西。
简单地调用std::terminate是一个非常糟糕的语言设计选择,因为它没有把任何责任交给编写noexcept方法的人。相反,它甚至使用户无法解决问题,从而损害了软件的重用。
例如,假设我是一个糟糕的库开发人员,我写了以下代码:
我的库附带的头文件Foo.h:
class Foo
{
public:
void DoSomething() noexcept;
};
你是一个快乐的消费者 fooolib 编写一个Bar应用程序:
Bar.cpp
#include "Foo.h"
int main()
{
Foo foo;
try
{
foo.DoSomething();
}
catch (...)
{
std::cout << "Yay!" << std::endl;
}
return 0;
}
代码编译得很好,运行得很好,直到你让Foo抛出异常…如果在try/catch块中包含对foo.DoSomething()的调用,则没有任何区别。代码将直接中止。
如果你没有Foo的代码,你不能修复它。在这种情况下,唯一的解决方案是扔掉Foo库,自己编写。
Foo.cpp的内容可以像这样:
static void PotentiallyThrowException()
{
throw 0;
}
void Foo::DoSomething() noexcept
{
PotentiallyThrowException();
}
注意,这取决于Foo::DoSomething()的实现者将自己的调用包装到try/catch中。但是由于同样的问题,如果他们调用其他标记为noexcept的方法,而这些开发人员没有这样做,那么现在它是Foo::DoSomething()。等等,等等。
我们可以有把握地说,从语义的角度来看,noexcept不仅无用,而且有害。
noexcept(true)
函数可以调用noexcept(false)
函数。如果抛出异常,将会出现运行时错误。为什么允许这样做的典型示例是:
double hypotenuse(double opposite, double adjacent) noexcept(true)
{
return std::sqrt(opposite*opposite + adjacent*adjacent);
}
std::sqrt
将抛出domain_error
,如果它的参数是负的,但显然这不会在这里发生。
(在理想的情况下,默认情况下,exception_cast
允许它在需要时被禁止。如果抛出异常,结果可能是UB,或者std::terminate)。
在函数类型中包含异常说明与函数是否可以调用具有不兼容异常说明的另一个函数是正交的(不处理异常说明中未包含的异常)。前者是关于函数指针的类型安全(因此,您不能通过已知不会抛出的函数指针调用抛出函数)。对于后者,我们可以在编译期间禁止它(如在Java中),或者将其视为运行时错误(导致程序终止,如c++当前标准所选择的那样)。
可以类比为从const
(非静态)成员函数调用非const
(非静态)成员函数。然而,不同之处在于间接修改通过const
成员函数调用的非const
成员函数内部的对象将无法检测到(或者检测成本太高),并可能导致严重的错误,这就是为什么必须在编译期间防止它。然而,抛出异常的行为是(应该是)一个异常事件,我们可以在运行时插入检查,检查异常是否符合异常规范,是否应该释放,或者它违反了程序逻辑,是否应该终止程序。
- 函数调用中参数的顺序重要吗
- 基于另一个成员参数将函数调用从类传递给它的一个成员
- 变量没有改变?通过向量的函数调用
- 在两个类中共享相同的函数调用,并在不需要时避免空实例化
- 是否有C++编译器选项允许激进地删除所有函数调用,并将参数传递给具有空体的函数
- 我知道函数调用中存在歧义.有没有办法调用foo()函数
- 模板函数调用
- 获取从C++中同一类中的构造函数调用的方法返回的值
- 析构函数调用
- 成员函数调用和C++对象模型
- 使用共享指针的函数调用,其对象应为 const
- C++:编译时检查匹配的函数调用对?
- 函数调用C++中的参数太少
- 来自 DLL 的函数调用 [表观调用的括号前面的表达式必须具有(指向-)函数类型]
- 返回指向对象的指针的函数调用是否为 prvalue?
- C++ 如何重载 [] 运算符并进行函数调用
- 代码的效率. 转到和函数调用
- 是同一作用域的函数部分中的函数调用
- 如何封装一个函数,以便它只能由同一类中的一个其他函数调用?
- 类型擦除的std::function与虚拟函数调用的开销