从std::call_once抛出异常

Throwing an exception from std::call_once

本文关键字:once 抛出异常 call std      更新时间:2023-10-16

c++标准对抛出异常(§30.4.4.2/2)的函数std::call_once的执行作了如下规定:

2/Effects: call_once的执行如果不调用它的函数是被动执行。调用call_once函数的执行是活动执行。主动执行应该调用INVOKE (DECAY_- COPY (std::forward(func)), DECAY_COPY (std::forward(args))…)。如果调用func抛出异常,则执行异常,否则返回。异常执行应将异常传播给call_once的调用者。对于给定的once_flag,在所有call_once的执行中,最多只能有一次返回执行;退回执行的,为最后一次主动执行;只有在有返回执行的情况下才会有被动执行。[注:被动执行允许其他线程可靠地观察早期返回执行产生的结果。]

我正在使用Visual Studio 2012并运行以下代码:

void f(){
    throw std::exception( "Catch me!" );
}
int main( int argc, char* argv[] ){
    once_flag flag;
    try{
        call_once( flag, f );
    } catch( const std::exception& e ){
        cout << e.what() << endl;
    }
    return 0;
}

我的结果是:catch块中的代码运行并打印消息,但是当程序存在时,我得到对abort()的调用,并将以下消息打印到cout:

…mutex.c(38)互斥锁在繁忙时被破坏

这应该发生吗?

这应该发生吗?

不,不完全是。这是一个错误

但是,请注意,VC11并不是唯一这样做的:

  • Intel ICC 13.0.1调用std::terminate(),如果你的异常没有处理(见实际例子);
  • GCC 4.8.0 beta可能做了类似的事情,但它不显示任何输出,它只是吞下异常并静默地终止程序(参见实际示例)。[更新:此错误似乎不能在其他环境中重现,很可能是liveworkspace.org上的配置问题]
另一方面,GCC 4.7.2和Clang 3.2的行为是正确的。

顺便说一下,值得注意的是c++标准(第18.8.1段)规定std::exception只有一个默认构造函数和一个复制构造函数。你正在使用的构造函数很可能是一个不可移植的MS扩展。

您可以考虑使用std::logic_error,它派生自std::exception,并支持接受字符串的构造函数。