含非平凡析构成员的联合体能否拥有constexpr析构?编译器差异及方案问询
联合体含非平凡析构成员时的constexpr析构问题
问题说明
当联合体包含拥有非平凡析构函数的成员时,能否为该联合体声明constexpr析构函数?以下示例代码在GCC中可编译通过,但Clang拒绝编译:
struct Foo { int x, y; ~Foo() {} // 非平凡析构函数 }; union U { char dummy; Foo f; constexpr ~U() {} // 尝试声明constexpr析构函数 }; consteval void bar() { U u{}; // 在consteval函数中创建U对象 }
Clang的报错信息如下:
<source>:10:15: error: destructor cannot be declared constexpr because data member 'f' does not have a constexpr destructor constexpr ~U() {} ^ <source>:8:9: note: data member 'f' declared here Foo f; ^ <source>:13:16: error: consteval function never produces a constant expression [-Winvalid-constexpr] consteval void bar() { ^ <source>:14:8: note: non-literal type 'U' cannot be used in a constant expression U u{}; ^
标准符合性判断
Clang的处理符合C标准。根据C标准对constexpr析构函数的要求:类的所有非静态数据成员的析构函数必须是constexpr析构函数(或平凡析构函数,平凡析构默认满足constexpr要求)。此处Foo的析构函数是非平凡且非constexpr的,因此联合体U不能声明constexpr析构函数。GCC的处理属于放宽了标准约束,不符合规范。
修改方案
要在保留Foo非平凡析构函数的前提下,让bar函数中U u{}的定义合法,可采用以下两种方式:
方式1:将Foo的析构函数改为constexpr(允许微调Foo)
非平凡析构函数也可以是constexpr的,修改Foo的析构函数后,U的constexpr析构函数即可满足标准要求:
struct Foo { int x, y; constexpr ~Foo() {} // 非平凡但constexpr的析构函数 }; union U { char dummy; Foo f; constexpr ~U() {} }; consteval void bar() { U u{}; [[maybe_unused]] auto addr = &u.f.y; // 合法获取地址 }
方式2:不声明U的constexpr析构,改用编译期偏移量计算(严格不修改Foo)
若完全不能修改Foo,可通过offsetof宏直接计算Foo::y的偏移量,无需在consteval中创建U对象并访问成员:
#include <cstddef> struct Foo { int x, y; ~Foo() {} }; union U { char dummy; Foo f; }; consteval ptrdiff_t get_y_offset() { return offsetof(Foo, y); // 编译期计算偏移量,等效于&u.f.y - &u }
若必须创建U对象并直接获取u.f.y的地址,目前在严格遵循C++标准的前提下无法实现——因为U无法成为字面量类型(析构函数无法满足constexpr或平凡的要求),而consteval函数中只能使用字面量类型的变量。
内容的提问来源于stack exchange,提问作者geza
相关产品推荐
相关产品推荐

