可以使用
std::construct_at
复制前一个活动成员的值来更改联合体中的活动成员吗?
这个最小的例子
#include <memory>
constexpr int f() {
union U {
char x{0};
int y;
} u;
std::construct_at(&u.y, u.x);
return u.y;
}
static_assert( f() == 0 );
被 GCC 和 MSVC 接受,但 Clang 拒绝它并出现错误:
/opt/compiler-explorer/clang-19.1.0/bin/../include/c++/v1/__memory/construct_at.h:41:50:
note: read of member 'x' of union with active member 'y' is not allowed in a constant expression
41 | return ::new (std::__voidify(*__location)) _Tp(std::forward<_Args>(__args)...);
在线演示:https://gcc.godbolt.org/z/xrj57jroj
这里哪种实现是正确的?
CWG 2721 的决议阐明,当分配函数返回时且在评估
new
初始值设定项之前,存储被视为由 new
重用。
当存储被重用时,前一个对象的生命周期结束,并且不再活跃。
因此 Clang 是正确的。
std::construct_at
通过引用获取参数并将它们转发到 new
初始值设定项。左值到右值的转换应用于 u.x
,以便仅在初始化器求值期间获取存储的值。此时存储已被重用,u.x
的生命周期已结束。
这可以通过写作来解决
std::construct_at(&u.y, auto(u.x));
或
auto v = u.x;
std::construct_at(&u.y, v);
相反。