C++ 字符数组的严格别名和并集

C++ 字符数组的严格别名和并集,c++,c++14,strict-aliasing,C++,C++14,Strict Aliasing,我有点期待答案会是什么,但我很好奇标准对此有何规定 设置:我想控制结构中字段的精确偏移量,并直接在字段类型中指定它们。这就是神奇之处: #include <type_traits> #include <cstdint> #include <cstring> #include <new> template <uint32_t OFFSET, typename T> struct FieldOverlay { static_ass

我有点期待答案会是什么,但我很好奇标准对此有何规定

设置:我想控制结构中字段的精确偏移量,并直接在字段类型中指定它们。这就是神奇之处:

#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));
#包括
#包括
#包括
#包括
模板
结构域覆盖
{
静态_断言(std::is_triple::value,“只能与普通类型一起使用”);
FieldOverlay()=删除;
FieldOverlay(FieldOverlay&其他)=删除;
FieldOverlay(FieldOverlay常量和其他)=删除;
FieldOverlay&operator=(T const&val){new(buf+OFFSET)T(val);返回*this;}
算子T()
{
电视
::memcpy(&v,buf+偏移量,sizeof(T));
返回v;
}
私人:
字符buf[偏移量+大小f(T)];
};
//精确控制构件偏移
联合我的讯息
{
fieldx;
田野调查;
};
void示例用法(MyMessage&m)
{
m、 y=m.x;
}
结构MyMessageEquivalent
{
uint32_t x;
字符填充[3];
uint32_t y;
}_uuu属性_uuu((压缩));
这在gcc 6.3上编译,具有
-O3-std=c++1z-fstrict别名-Wall-Wpedantic-Wextra-Werror
,没有任何错误,并按预期工作。(见导栓:)


问:这符合犹太标准吗?我认为它变得非常危险,因为工会中没有一个“活跃”成员
MyMessage
。但是,由于所有内容都是通过
char
数组访问的,这有助于严格的别名规则吗?

您的代码有很多问题,但在严格别名成为问题之前很久,您就会调用UB

MyMessage::x
MyMessage::y
是与布局不兼容的类型。它们也没有共同的初始序列。是的,尽管它们都存储一个
char
数组,但它们不存储相同大小的
char
数组。这两个数组的长度不同,在通用的初始序列规则中没有任何内容表明,包含两个相同基类型但大小不同的数组的两个结构具有相同的初始序列

因此,当
y
是联盟的活动成员时,您无法尝试访问
x
。反之亦然

仅供参考:你重新诠释的演员阵容也会激怒UB。该内存中没有
T
,并且
reinterpret\u cast
无法创建对象。因此,像访问包含
T
的内存一样访问该内存违反了标准。此外,标准不允许通过未对齐的指针访问对象


因此,基本上,您试图做的事情永远不会起作用。

您的代码有很多问题,但在严格别名成为问题之前很久,您就会调用UB

MyMessage::x
MyMessage::y
是与布局不兼容的类型。它们也没有共同的初始序列。是的,尽管它们都存储一个
char
数组,但它们不存储相同大小的
char
数组。这两个数组的长度不同,在通用的初始序列规则中没有任何内容表明,包含两个相同基类型但大小不同的数组的两个结构具有相同的初始序列

因此,当
y
是联盟的活动成员时,您无法尝试访问
x
。反之亦然

仅供参考:你重新诠释的演员阵容也会激怒UB。该内存中没有
T
,并且
reinterpret\u cast
无法创建对象。因此,像访问包含
T
的内存一样访问该内存违反了标准。此外,标准不允许通过未对齐的指针访问对象


因此,基本上,您所尝试的操作永远不会起作用。

“但是,由于所有内容都是通过字符数组访问的”,我看到了很多类型转换,这表明“所有内容”不是通过
char
数组来访问的。@Nicolas我的意思是所有这些值的最终存储都在
char
数组中。好吧,一个联合体在任何时候都只能有一个成员处于活动状态。如果您违反了这一点,那么它就是UB。像
T&asT(){return*reinterpret_cast(buf+OFFSET)}
这样的行可能无效,因为
buf+OFFSET
处的内存最初不是
T
@NathanOliver的类型,从字面上说是的。因为这是C++,所以是UB。如果它是C99,那么它就是IB,因为它显式地允许联合类型双关。但实际上,我并不知道任何对C和C++的编译器都不同。所有这些都将应用C99规则并允许联合类型双关语。“但是,由于所有内容都是通过字符数组访问的”,我看到了很多类型转换,这表明“所有内容”都不是通过
char
数组访问的。@Nicolas我的意思是,所有这些值的最终存储都在
char
数组中。嗯,同一时间,工会中只能有一个成员处于活动状态。如果您违反了这一点,那么它就是UB。像
T&asT(){return*reinterpret_cast(buf+OFFSET)}
这样的行可能无效,因为
buf+OFFSET
处的内存最初不是
T
@NathanOliver的类型,从字面上说是的。因为这是C++,所以是UB。如果它是C99,那么它就是IB,因为它显式地允许联合类型双关。但实际上,我并不知道任何对C和C++的编译器都不同。它们都将应用C99规则并允许联合类型双关。我已经更改了代码以避免使用
重新解释cast
。但我想根据标准,工会的事情仍然是UB。我希望有一个条款,为琐碎类型的联合。。。特别是因为在这种情况下,我从不访问用于其他字段的内存,因此“相关”部分不会重叠。我更改了代码,以避免使用
reinterpret\u cast