编译时检测不应该抛出但仍然抛出的函数(没有except或替代方法)

Compile time detection of functions that should not throw but still do (noexcept or alternative ways)

本文关键字:except 没有 方法 不应该 检测 编译 函数      更新时间:2023-10-16

我知道noexcept只是提供信息,gcc/clang不会触发任何警告/错误:

void foo() noexcept
{
  throw 1;
}
int main()
{
  foo();
  return 0;
}

所以-有没有一种方法或技术来标记一个函数不抛出任何东西,实际上得到编译时的警告/错误从编译器,如果实现抛出?

一个简单的用例——我有一个框架,它公开了一组客户端插件应该实现的接口,然后框架从共享对象加载插件,通过工厂实例化接口并调用接口方法。我希望在调用期间没有异常抛出库边界,所以我想标记由插件实现的接口中的所有函数不抛出,而不是依赖于具有try/catch块内部的实现。有什么常见的技术/替代解决方案吗?

我也明白,没有编译时检查可以检测到由于无效数据在运行时可能发生的潜在结构化异常,但至少有c++异常工作吗?

编辑

感谢Jason的评论,我检查了函数指针发生了什么,也让我失望了一点——clang至少抱怨了下面的代码片段,gcc没有:

void foo() noexcept
{
  throw 1;
}
void bar() noexcept(false)
{
}
int main()
{
  void (*safe_function_one)() noexcept  = &foo;  
  void (*safe_function_two)() noexcept  = &bar;  
  return 0;
}

请记住,noexcept是向编译器提示,它不需要提供在这个特定函数中适当展开堆栈帧的机制(以防止出现异常),从而允许它进行更多的优化。如果抛出异常,则可能无法进行适当的恢复。

std::move_if_noexcept是noexcept的关键。对于使用move语义的特定情况,Noexcept是必需的,这里有描述。

正如您指出的那样,在编译时无法确定无效数据。虽然在函数的根作用域中检测'throw'是微不足道的,但分析可能从该函数调用的所有可能的代码路径以确定是否抛出异常则需要更多的工作。我怀疑所有的编译器都有这个问题,并且都在努力寻找解决方案,但是noexcept从来都不是保证,所以我怀疑这是一个相当有激情的争论。

现在,您可能希望用noexcept修饰函数,并允许编译器生成更快的代码,并且非常强烈地告知抛出异常将产生不稳定的行为。然而,编译器无论如何都可以执行其中的一些优化,即使没有noexcept。例如,编译器可以很容易地确定许多内联函数是很好的候选函数。

编辑:

正如@Creris正确指出的那样,如果无法找到匹配的异常处理程序(这是非常不希望的),std::terminate()将被立即调用,但这当然不会阻止开发人员在这些情况下使用throw。