constexpr析构函数要求类所有成员及基类析构均可constexpr调用,且无运行期操作;C++20前不可显式声明,虚析构函数永远不满足条件。

constexpr 析构函数的隐式要求
一个类要能被 constexpr 对象在常量表达式中安全销毁,其析构函数必须是 constexpr 的——但 C++ 标准并不允许你显式给析构函数加 constexpr 说明符(C++20 起才允许,且有严格限制)。所以真正起决定作用的是:编译器是否能为该类**隐式生成 constexpr 析构函数**。
隐式生成的前提是:类的所有非静态成员、基类的析构函数都可 constexpr 调用,且析构函数体不执行任何运行期操作(比如调用非 constexpr 函数、访问 volatile 对象、new/delete、throw 等)。
- 若类含
std::string、std::vector、std::unique_ptr等标准容器/智能指针,其析构函数在 C++20 前**不是 constexpr**,因此整个类无法用于 constexpr 上下文的销毁 - 若类含自定义成员,需确认其析构函数是否满足 constexpr 条件(例如:仅含空操作或对字面量类型的简单赋值)
- 虚析构函数(
virtual ~T())**永远无法是 constexpr**(因涉及运行时虚表查找),哪怕函数体为空
如何实测一个类是否支持 constexpr 析构
最直接的办法是尝试在一个必须求值于编译期的上下文中构造再销毁它,让编译器报错说话:
struct Test {
constexpr Test() = default;
// ~Test() 隐式生成
};
constexpr Test make_and_destroy() {
Test t;
return t; // 返回会触发 t 的析构(C++17 NRVO 不影响此判定)
}如果编译失败并提示类似 call to non-constexpr function 或 temporary of non-literal type,大概率就是析构不可 constexpr。
立即学习“C++免费学习笔记(深入)”;
- Clang 报错常含
destructor is not constexpr - GCC 错误可能更隐晦,如
variable 't' cannot be defined in a constexpr function,本质还是析构卡住 - 注意:C++20 前,即使类无成员、无基类,若声明了
= default以外的析构函数(哪怕空实现),也会导致隐式 constexpr 析构失效
C++20 中显式 constexpr 析构函数的使用边界
C++20 允许写 constexpr ~T(),但这不是“放开限制”,而是把原本隐式规则显式化,并增加一条硬性约束:析构函数体内**不能有任何可能导致副作用的操作**,且所有被调用的析构函数也必须是 constexpr。
- 以下写法合法(C++20):
struct TriviallyDestructible { int* p = nullptr; constexpr ~TriviallyDestructible() { p = nullptr; } // OK: 仅字面量类型赋值 }; - 以下写法非法:
struct Bad { std::string s; constexpr ~Bad() {} // ❌ std::string::~string 不是 constexpr(C++20 中仍不是) }; - 即便写了
constexpr ~T(),若其调用了非 constexpr 成员析构,整个类仍不能用于constexpr对象生命周期管理
容易被忽略的间接破坏点:隐式移动与临时对象寿命
即使类本身满足 constexpr 析构,实际使用中仍可能因语义转换失败。典型场景是返回局部 constexpr 对象:
- 若类未定义
constexpr移动构造函数(且不满足隐式生成条件),return t;中的移动构造可能失败,进而使析构不被纳入常量求值路径 - 聚合类(aggregates)在 C++20 后支持
constexpr析构,但若含引用成员或 const 成员且未正确初始化,仍会导致隐式析构不可用 - 模板类(如
template<typename t> struct Box { T x; };</typename>)是否可 constexpr 析构,完全取决于T是否满足——这点极易在泛型代码中漏检
最稳妥的做法始终是:用 constexpr 变量绑定 + 编译期断言双重验证,而不是只看类定义。


















