指针可互换类型和联合数组
Pointer-Interconvertible Types and Arrays of Unions
如果我有 union
.
struct None {};
template<typename T>
union Storage {
/* Ctors and Methods */
T obj;
None none;
};
pointer-interconvertible
类型表示执行以下转换是合法的:
Storage<T> value(/* ctor args */);
T* obj = static_cast<T*>(static_cast<void*>(&value));
把Storage<T>
的数组当成T
的数组是合法的吗?
Storage<T> values[20] = { /* initialisation */ };
T* objs = static_cast<T*>(static_cast<void*>(values));
for(auto i = 0; i < 20; ++i) {
objs[i].method(); // Is this pointer access legal?
}
不,这是不合法的。关于 pointer-arithmetic 唯一可以被视为数组的是数组(以及由不是数组元素的对象形成的假想 single-element 数组)。所以这里与objs[i]
中的指针运算相关的“数组”是由第一个数组元素的obj
形成的假设的single-element数组,因为它本身不是数组的元素。对于 i >= 1
,objs[i]
不会指向一个对象,因此可能不会在其上调用 method
。
实际上,如果 T
的大小和联合的大小不一致,就会出现问题,因为在这种情况下,地址的算术运算也会失效。不能保证这两个大小一致(即使 None
有 sizeof
和 alignof
等于 1
)。
除了那个问题,我怀疑编译器是否真的利用这种未定义的行为来进行优化。不过我不能保证。
另请注意,如果obj
是联合体的活跃成员,则只能通过强制转换获得的指针访问obj
,这意味着obj
是成员在示例中已初始化。
你表示你打算在常量表达式中使用它,在这种情况下,编译器需要诊断未定义的行为,并且可能会拒绝这样的程序,而不管优化器的实际考虑如何。
此外,在常量表达式中,不允许从 void*
强制转换为不同的对象类型(或 reinterpret_cast
)。所以 static_cast<T*>(static_cast<void*>(values));
无论如何都会导致它失败。尽管这可以通过直接将指针指向联合成员(例如 &values[0].obj
)来简单地补救。没有理由在这里使用强制转换。
如果我有 union
.
struct None {};
template<typename T>
union Storage {
/* Ctors and Methods */
T obj;
None none;
};
pointer-interconvertible
类型表示执行以下转换是合法的:
Storage<T> value(/* ctor args */);
T* obj = static_cast<T*>(static_cast<void*>(&value));
把Storage<T>
的数组当成T
的数组是合法的吗?
Storage<T> values[20] = { /* initialisation */ };
T* objs = static_cast<T*>(static_cast<void*>(values));
for(auto i = 0; i < 20; ++i) {
objs[i].method(); // Is this pointer access legal?
}
不,这是不合法的。关于 pointer-arithmetic 唯一可以被视为数组的是数组(以及由不是数组元素的对象形成的假想 single-element 数组)。所以这里与objs[i]
中的指针运算相关的“数组”是由第一个数组元素的obj
形成的假设的single-element数组,因为它本身不是数组的元素。对于 i >= 1
,objs[i]
不会指向一个对象,因此可能不会在其上调用 method
。
实际上,如果 T
的大小和联合的大小不一致,就会出现问题,因为在这种情况下,地址的算术运算也会失效。不能保证这两个大小一致(即使 None
有 sizeof
和 alignof
等于 1
)。
除了那个问题,我怀疑编译器是否真的利用这种未定义的行为来进行优化。不过我不能保证。
另请注意,如果obj
是联合体的活跃成员,则只能通过强制转换获得的指针访问obj
,这意味着obj
是成员在示例中已初始化。
你表示你打算在常量表达式中使用它,在这种情况下,编译器需要诊断未定义的行为,并且可能会拒绝这样的程序,而不管优化器的实际考虑如何。
此外,在常量表达式中,不允许从 void*
强制转换为不同的对象类型(或 reinterpret_cast
)。所以 static_cast<T*>(static_cast<void*>(values));
无论如何都会导致它失败。尽管这可以通过直接将指针指向联合成员(例如 &values[0].obj
)来简单地补救。没有理由在这里使用强制转换。