我有一个可以减少的功能:
void f() {
static MyObject o("hello");
DoSomethingWith(o);
}
这个函数是在一个C API边界调用的,所以就像一个好孩子一样,我使用try
来捕获在越过边界之前抛出的任何异常并搞砸了:
void f() {
try {
static MyObject o("hello");
DoSomethingWith(o);
} catch (const MyObjectException& e) {
Message("Constructor of o failed");
}
}
第一次调用此函数,我收到消息"Constructor of o failed"
。但是,稍后,再次调用该函数,我再次收到该消息。我在调用f
时多次收到消息。我正在使用Visual C ++,所以这告诉我MSVC ++的作用,但不是应该做什么。
我的问题是,当static
函数变量的构造函数异常终止(通过throw
,一个longjmp
从构造函数中终止时,应该发生什么,终止它的线程在,等)?此外,在它之前和之后声明的任何其他static
变量应该发生什么?我也很感谢标准中的任何相关引用。
答案 0 :(得分:6)
C ++ 11标准的第6.7节([stmt.dcl]
)声明
在执行任何其他初始化之前,将执行具有静态存储持续时间(3.7.1)或线程存储持续时间(3.7.2)的所有块范围变量的零初始化(8.5)。具有静态存储持续时间的块范围实体的常量初始化(3.6.2)(如果适用)在首次输入块之前执行。允许实现使用static或者执行其他块范围变量的早期初始化 线程存储持续时间与允许实现在命名空间范围内静态初始化具有静态或线程存储持续时间的条件相同的条件(3.6.2)。否则,在第一次控制通过其声明时初始化这样的变量; 这个变量在初始化完成后被认为是初始化的。如果初始化通过抛出异常退出,则初始化 未完成,因此下次控制进入声明时将再次尝试。如果控件在初始化变量时同时进入声明,则并发执行应等待初始化完成。 如果控制在变量存在时递归地重新输入声明 初始化后,行为未定义。
答案 1 :(得分:2)
问:当静态函数变量的构造函数异常终止时会发生什么?
答:
§6.7 [stmt.dcl] p4
[...]否则这个变量在第一次控制通过其声明时被初始化;这样的变量在初始化完成后被认为是初始化的。如果初始化通过抛出异常退出,则初始化未完成,因此下次控件进入声明时将再次尝试。
因此,如果o
通过抛出异常退出,则会再次尝试初始化§3.6.3 [basic.start.term]
。 我认为同样适用于初始化的任何异常退出,尽管没有明确说明。 Brb,寻找更多报价。
由于我找不到任何相关内容,因此我打开了a follow-up question。
问:在它之前和之后声明的任何其他静态变量会发生什么?
答:没有,只要线程或整个程序都没有终止。
main
初始化对象(即,其生命周期(3.8)已开始的对象)具有静态存储持续时间的析构函数(12.4)被调用为从
std::exit
返回并且由于调用{{1}而导致的结果(18.5)。具有给定线程内的线程存储持续时间的初始化对象的析构函数被调用作为从该线程的初始函数返回并且作为该线程调用std::exit
的结果。在启动具有静态存储持续时间的任何对象的析构函数之前,对该线程中具有线程存储持续时间的所有初始化对象的析构函数的完成进行排序。
§3.7.2 [basic.stc.thread]
具有线程存储持续时间的变量应在其第一次使用odr(3.2)之前初始化,如果构造,则应在线程退出时销毁。
答案 2 :(得分:-1)
重新设计您的计划。将尝试初始化静态变量,直到初始化成功为止。如果这种模式不适合,你应该找到一种更好的方式来表达你的目标。也许是您在受控环境中填充的静态unique_ptr
?如果您有一个无法可靠构造的资源,则必须将构造重新放入可以处理错误的其他上下文中,或者使您的函数仅依赖于资源(例如,通过空指针)。