我有点期待答案会是什么,但我很好奇标准对此的看法。
设置:我想控制结构中字段的确切偏移量,并直接在字段类型中指定它们。这是魔术:
#include <type_traits>
#include <cstdint>
#include <cstring>
#include <new>
template <uint32_t OFFSET, typename T>
struct FieldOverlay
{
static_assert(std::is_trivial<T>::value, "Can only be used with trivial types");
FieldOverlay() = delete;
FieldOverlay(FieldOverlay&& other) = delete;
FieldOverlay(FieldOverlay const& other) = delete;
FieldOverlay& operator = (T const& val) { new (buf + OFFSET) T(val); return *this; }
operator T()
{
T v;
::memcpy(&v, buf + OFFSET, sizeof(T));
return v;
}
private:
char buf[OFFSET + sizeof(T)];
};
// Precisely control member offsets
union MyMessage
{
FieldOverlay<0, uint32_t> x;
FieldOverlay<7, uint32_t> y;
};
void exampleUsage(MyMessage& m)
{
m.y = m.x;
}
struct MyMessageEquivalent
{
uint32_t x;
char padding[3];
uint32_t y;
} __attribute__ ((packed));
这在gcc 6.3上编译-O3 -std=c++1z -fstrict-aliasing -Wall -Wpedantic -Wextra -Werror
没有任何错误,并按预期工作。 (见godbolt:https://godbolt.org/g/DHWLD9)
问题:这个犹太洁食是按照标准吗?我认为它变得非常冒险,因为联盟中没有一个“活跃”的成员MyMessage
。但是,由于通过char
数组访问所有内容,这有助于严格别名规则吗?
答案 0 :(得分:2)
您的代码存在很多问题,但在严格别名出现问题之前,您将调用UB long 。
MyMessage::x
和MyMessage::y
不是布局兼容类型。它们也没有共同的初始序列。是的,尽管它们都存储了一个char
数组,但它们并不存储{em>相同大小的 char
数组。这两个数组具有不同的长度,并且在公共初始序列规则中没有任何内容表明包含两个相同基类型但具有不同大小的数组的两个结构具有共同的初始序列。
因此,当x
是联盟的活跃成员时,您无法尝试访问y
。反之亦然。
和我们:你的重新诠释也会激怒UB。该内存中没有T
,reinterpret_cast
无法创建对象。因此,访问该内存就好像它包含T
一样违反了标准。此外,该标准不允许您通过未对齐的指针访问对象。
所以基本上,你要做的就是不会工作。