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

如何通过static_assert禁止构造/析构函数调用指定成员方法

问题背景

现有如下类定义,需要实现编译期限制:禁止模板成员方法foo()在构造函数、析构函数内被调用,违规调用时直接触发静态断言失败。

template <typename SELF>
class Base {
protected:
    template <int X>
    void foo();
};

class Child : public Base<Child> {
public:
    Child() {
        // 预期此处触发static assertion failure
        foo<10>();
    }
    ~Child() {
        // 预期此处调用也触发static assertion
    }
};
实现方案

该需求可以通过编译期上下文检测实现,无运行时开销,且不需要修改现有foo()的调用写法。

C++20 可用实现(当前主流编译器已支持)

利用C++20标准库的std::source_location捕获调用点的函数签名,通过consteval函数在编译期解析签名,判断调用点是否处于构造/析构函数中,触发静态断言:

#include <source_location>
#include <string_view>

// 编译期校验:判断调用点是否位于SELF类型的构造/析构函数内
template <typename SELF>
consteval bool is_call_forbidden(std::string_view func_sig) {
    // 适配GCC/Clang提取SELF的纯类名,适配MSVC仅需修改该段提取逻辑
    constexpr std::string_view pretty_func = __PRETTY_FUNCTION__;
    constexpr std::string_view prefix = "SELF = ";
    constexpr auto pos = pretty_func.find(prefix);
    constexpr std::string_view class_name = pretty_func.substr(
        pos + prefix.size(),
        pretty_func.find(';', pos) - (pos + prefix.size())
    );

    // 从调用签名中定位当前函数名
    const auto class_pos = func_sig.find(class_name);
    if (class_pos == std::string_view::npos) return false;
    const auto scope_pos = func_sig.find("::", class_pos);
    if (scope_pos == std::string_view::npos) return false;
    const auto func_start = scope_pos + 2;
    const auto func_end = func_sig.find('(', func_start);
    const std::string_view func_name = func_sig.substr(func_start, func_end - func_start);

    // 构造函数名与类名一致,析构函数名为~加类名
    return func_name == class_name || func_name == std::string_view("~") + class_name;
}

template <typename SELF>
class Base {
protected:
    template <int X>
    constexpr void foo(const std::source_location& loc = std::source_location::current()) {
        static_assert(!is_call_forbidden<SELF>(loc.function_name()),
            "禁止在构造函数或析构函数中调用foo()");
        // 此处填写foo的原有业务逻辑
    }
};

// 测试用例
class Child : public Base<Child> {
public:
    Child() {
        foo<10>(); // 编译报错,触发静态断言
    }
    void normal_call() {
        foo<10>(); // 正常编译通过
    }
    ~Child() {
        foo<10>(); // 编译报错,触发静态断言
    }
};

该实现已适配GCC 11+、Clang 14+,适配MSVC仅需调整类名提取逻辑匹配__FUNCSIG__的输出格式即可。

C++23/26 标准实现(无编译器兼容逻辑)

未来C26标准将提供std::is_constructing_v、std::is_destructing_v类型特征,结合C23的显式对象参数(deducing this),可以直接在编译期判断对象生命周期状态,不需要手动解析函数签名,实现更简洁可靠:

#include <type_traits>

template <typename SELF>
class Base {
protected:
    template <int X, typename Self = SELF>
    constexpr void foo(this Self& self) {
        static_assert(!std::is_constructing_v<Self> && !std::is_destructing_v<Self>,
            "禁止在构造函数或析构函数中调用foo()");
        // 此处填写foo的原有业务逻辑
    }
};

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.03 05:01:00