You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

为何std::scope_success析构函数要检查未捕获异常数≤构造时的值?

为什么std::scope_success析构函数用“小于或等于”而非“等于”?

答案很明确:确实存在析构时std::uncaught_exceptions()数值低于构造时的场景,最典型的就是「在栈展开过程中创建scope_success,之后异常被捕获处理」的情况。

举个实际代码例子就能明白:

#include <experimental/scope>
#include <iostream>
#include <stdexcept>

void inner() {
    throw std::runtime_error("test error");
}

void middle() {
    struct LocalObj {
        ~LocalObj() {
            // 此时处于栈展开阶段,异常尚未被捕获,std::uncaught_exceptions()返回1
            std::experimental::scope_success guard{[](){
                std::cout << "scope_success回调执行\n";
            }};
            // guard构造时,未捕获异常数是1
        }
    };

    try {
        LocalObj obj;
        inner(); // 抛出异常,触发栈展开,先析构obj
    } catch (const std::exception&) {
        // 异常被捕获,std::uncaught_exceptions()回到0
        std::cout << "异常在middle中被处理\n";
    }
}

int main() {
    try {
        middle();
    } catch (...) {}
}

这个例子的流程是:

  1. 进入middle()的try块,构造LocalObj实例。
  2. 调用inner()抛出异常,未捕获异常数变为1,开始栈展开,调用LocalObj的析构函数。
  3. 在析构函数里构造scope_success,此时未捕获异常数是1。
  4. LocalObj析构完成,回到catch块,异常被捕获,未捕获异常数降为0。
  5. catch块执行完毕,scope_success的生命周期结束,开始析构——此时未捕获异常数是0,明显小于构造时的1。

如果scope_success的析构函数只检查「等于」,那这个场景下回调就不会执行,但我们显然希望它执行:毕竟scope_success的设计初衷就是保证在对象生命周期结束时,只要不是因为新的未捕获异常导致的栈展开,就触发回调。

用「小于或等于」的逻辑,就能覆盖所有合理的执行场景:

  • 正常退出时,析构未捕获数等于构造时的数值(都是0),触发回调;
  • 栈展开全程未捕获异常时,析构未捕获数等于构造时的数值(都是N>0),触发回调;
  • 栈展开中途异常被捕获,析构未捕获数小于构造时的数值,依然触发回调。

而「大于」的情况不可能出现:未捕获异常数只会在抛出时增加,捕获时减少;析构函数执行时如果抛出新异常,程序会直接终止,不会走到检查逻辑。

内容的提问来源于stack exchange,提问作者tmlen

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.14 10:03:38