提问人:user3188445 提问时间:11/13/2023 最后编辑:user17732522user3188445 更新时间:11/13/2023 访问量:90
您可以在 C++20 constexpr 构造函数中隐式激活联合的数组成员吗?
Can you implicitly activate array member of union in a C++20 constexpr constructor?
问:
我正在使用 C++20 并且有一个包含包含数组的透明联合的结构。我需要我的结构有一个构造函数,但我希望避免在上下文中未调用构造函数的情况下填充整个数组。Gcc 允许我这样做,而 clang 则不然。constexpr
constexpr
下面是一个最小的示例:
#include <algorithm>
#include <type_traits>
struct test {
union {
char buf_[15];
};
// Both gcc and clang accept:
// constexpr test() : buf_{} { fillbuf(); }
// Gcc accepts and clang rejects:
constexpr test() { fillbuf(); }
// Clang accepts and gcc rejects
// constexpr test() {}
constexpr void fillbuf() {
if (std::is_constant_evaluated())
std::fill_n(buf_, sizeof(buf_), 0);
}
};
constinit test mytest{};
对于第二个构造函数(我想要的构造函数),clang 抱怨我的构造函数不是因为“在常量表达式中不允许将函数分配给没有活动成员的联合的成员'buf_'。constexpr
请注意,对于第三个构造函数(上面注释掉了),clang 接受了代码,而 gcc(我认为是明智的)抱怨“'test()' 不是一个常量表达式,因为它引用了一个未完全初始化的变量。
哪个编译器是正确的?有没有其他方法可以实现我的需求?在许多情况下,我只需要缓冲区的第一个字节,因此在非上下文中构造对象时,不希望填充整个内容的开销。constexpr
值得一提的是,我使用的是 gcc 13.2.1 和 clang 16.0.6。
答:
是的,您可以在常量表达式中激活一个普通的数组联合成员,就像在运行时使用 C++20 一样。
问题是
std::fill_n(buf_, sizeof(buf_), 0);
无法激活任何成员。唯一可以隐式启动对象生存期的表达式形式是内置或微不足道的赋值表达式,其左侧是对指定联合成员的表达式的(链)内置成员访问和数组索引操作。有关详细信息,请参见 [class.union.general]/6。
但是,由于将通过指针分配给工会成员,即不命名工会成员,因此它不符合该特殊规则的条件。因此,无论是在运行时还是在常量表达式中,它都无法启动联合成员的生存期,也无法使联合成员处于活动状态。调用在运行时具有未定义的行为。fill_n
fill_n
因此,您可以通过添加以下内容,轻松地在呼叫自己之前使成员处于活动状态:fill_n
buf_[0] = 0 /* or anything else */;
或直接使用循环赋值(而不是通过引用或指针)。buf_[i]
Clang 严格遵守这一规则,而 GCC 则过于宽容。
评论
uninitialized_fill
uninitialized_fill
new
如果程序尝试通过类型与以下类型之一不相似的 glvalue 访问对象的存储值,则行为未定义:
- 对象的动态类型,
- 与对象的动态类型相对应的有符号或无符号类型,或者
- 、 或 类型。
char
unsigned char
std::byte
因此,应将对象表示形式设置为所有零,而不使活动联合成员。std::fill_n(buf_, sizeof(buf_), 0);
*this
buf_
评论
std::variant
。union
std::variant