result_t work(resource_t& resource) {
lock_t ___(resource);
return work_impl(resource);
}
是否保证在返回___
后会调用的析构函数work_impl()
?还是编译器___
在调用之前可以自由销毁work_impl()
?
result_t work(resource_t& resource) {
lock_t ___(resource);
return work_impl(resource);
}
是否保证在返回___
后会调用的析构函数work_impl()
?还是编译器___
在调用之前可以自由销毁work_impl()
?
表达式work_impl(resource)
将被执行,结果将被复制到调用方或用作临时。然后,对象___
将被破坏。
另一方面,不要使用__
or___
作为任何标识符的前缀。它们是为编译器保留的。
如果析构函数不是平凡的,那么它可能不会被过早调用,前提是其余代码是正确的。在未定义行为的情况下(例如,具有两个或多个相邻的变量名_
),当然,不能保证。
如果无法区分差异,编译器可以随意做任何事情。但是如果析构函数有一些程序可见的效果,它总是会在work_impl
返回之后发生。
该程序的行为实际上是未定义的,标识符 __
是保留的,我们可以从 C++ 标准草案中的17.6.4.3
保留名称第2段中看到:
如果程序在保留名称的上下文中声明或定义名称,除非本条款明确允许,否则其行为未定义。
如果我们进一步查看17.6.4.3.2
Global names部分,它说:
每个包含双下划线 _ 或以下划线后跟大写字母 (2.12) 的名称都保留给实现以供任何使用。
因此,除非编译器文档__
可供用户代码免费使用,否则它是保留的。
隐式调用析构函数
因此,如果该程序没有调用未定义的行为,则隐式调用的析构函数的规则可以从标准草案12.4
Destructotrs第11段中获取,其中说(强调我的)
— 对于在程序终止 (3.6.3) 时具有静态存储持续时间 (3.7.1) 的构造对象,
— 对于在线程退出时具有线程存储持续时间 (3.7.2) 的构造对象,
—对于在其中创建对象的块退出 (6.7) 时具有自动存储持续时间 (3.7.3) 的构造对象,
— 对于临时对象的生命周期结束时构造的临时对象(12.2),
所以这意味着自动对象的析构函数将在您退出时被调用work()
,这必须在返回结果之后发生。我们可以进一步看到,对象被销毁的顺序也是从6.6
Jump 语句中指定的:
从范围退出时(无论如何完成),已在该范围内构建的具有自动存储持续时间 (3.7.3) 的对象将按其构建的相反顺序销毁。[注:对于临时性,见 12.2。——尾注]
请注意,包含双下划线__
或以下划线后跟大写字母开头的名称在任何范围内都是保留的。