如何通过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
相关产品推荐
相关产品推荐

