给定class.mem/28:
https://eel.is/c++draft/class.mem#general-28
其中规定:
在具有结构类型 T1 的活动成员的标准布局联合中,它 允许读取另一个联合体的非静态数据成员 m 结构类型 T2 的成员,前提是 m 是公共首字母的一部分 T1和T2的顺序;行为就像相应的成员 T1 获得提名。
我认为创建一个具有多个(不同)结构的联合是有效的,每个结构都有一个
uint64_t
成员:
template<some params>
struct CustomField
{
void operator=(uint64_t newVal)
{
val = someCustomInsertFn(newVal);
}
operator uint64_t() const
{
return someCustomExtractFn(val);
}
uint64_t val; // Note that this is used in a union and so aliases with all other fields and the `all` below
};
然后:
union CheckedBitField
{
struct { uint64_t all; }
CustomField<some param> fieldOne;
CustomField<some other param> fieldTwo;
};
这个想法是一个带有结构成员的简单联合,每个结构成员只有一个
uint64_t val
。
但是这些自定义字段具有自定义
void operator=(uint64_t newVal)
和 operator uint64_t() const
运算符,可以对基本位字段所能获得的内容添加额外的检查。
插入函数的一个示例是添加额外的检查,以确保您正在写入的值在逻辑上有效和/或不会被截断。
所以这个代码是有效的 - 就像在普通的位字段中一样:
CheckedBitField b{};
b.fieldOne = 5; // This runs custom code.
添加这些附加函数是否会违反 class.mem/28 规则和/或者我遗漏了什么?
完整的可运行代码 - 添加了设置字段的最小/最大值的功能: https://godbolt.org/z/ozz16GEP8
b.fieldOne = 5;
已经具有未定义的行为。
fieldOne
成员不是活动的,因此相应的成员子对象不在其生命周期内,因此在其上调用非静态成员函数是UB。
分配也不会启动生命周期并使成员处于活动状态,因为在这种情况下,
=
既不是内置运算符,也不是普通的赋值运算符。 (这是唯一的例外,不需要 new
或隐式创建对象的函数来更改联合的活动成员。)