c++ - 过度渴望使用constexpr进行C++ union 零初始化

标签 c++ constexpr zero-initialization

以下是带标签的联合模板“Storage”的简化示例,该模板可以假定联合中包含两种类型的L和R,以及指示它们已存储的 bool(boolean) 值。实例化使用两种不同大小的类型,较小的一种实际上是空的。

#include <utility>

struct Empty
{
};

struct Big
{
        long a;
        long b;
        long c;
};

template<typename L, typename R>
class Storage final
{
public:
        constexpr explicit Storage(const R& right) : payload{right}, isLeft{false}
        {
        }

private:
        union Payload
        {
                constexpr Payload(const R& right) : right{right}
                {
                }
                L left;
                R right;
        };

        Payload payload;
        bool isLeft;
};

// Toggle constexpr here
constexpr static Storage<Big, Empty> createStorage()
{
        return Storage<Big, Empty>{Empty{}};
}

Storage<Big, Empty> createStorage2()
{        
        return createStorage();
}
  • 构造函数使用Empty初始化R成员,并且仅调用该成员的联合构造函数
  • 联合从未默认初始化为整个
  • 所有构造函数都是constexpr

  • 因此,函数“createStorage2”应仅填充bool标签,而不要考虑联合。因此,我期望使用默认优化“-O”的编译结果:
    createStorage2():
            mov     rax, rdi
            mov     BYTE PTR [rdi+24], 0
            ret
    

    GCC和ICC都会生成类似
    createStorage2():
            mov     rax, rdi
            mov     QWORD PTR [rdi], 0
            mov     QWORD PTR [rdi+8], 0
            mov     QWORD PTR [rdi+16], 0
            mov     QWORD PTR [rdi+24], 0
            ret
    

    将整个32字节结构清零,而clang生成预期的代码。您可以使用https://godbolt.org/z/VsDQUu复制它。当您从“createStorage”静态函数中删除constexpr时,GCC将仅还原为bool标记的所需初始化,而ICC保持不变,并且仍然填充所有32个字节。

    这样做可能不是标准行为,因为未使用的位被“未定义”允许任何事情,包括被设置为零和消耗不必要的CPU周期。但这很烦人,如果您首先出于效率原因引入了工会,而您的工会成员(member)的人数相差很大。

    这里发生了什么?如果不是从构造函数和静态函数中删除constexpr的方法,是否有任何方法可以解决此问题?

    旁注:即使删除了所有constexpr,ICC似乎也会执行一些额外的操作,例如https://godbolt.org/z/FnjoPC:
    createStorage2():
            mov       rax, rdi                                      #44.16
            mov       BYTE PTR [-16+rsp], 0                         #39.9
            movups    xmm0, XMMWORD PTR [-40+rsp]                   #44.16
            movups    xmm1, XMMWORD PTR [-24+rsp]                   #44.16
            movups    XMMWORD PTR [rdi], xmm0                       #44.16
            movups    XMMWORD PTR [16+rdi], xmm1                    #44.16
            ret                                                     #44.16
    

    这些上浮指示的目的是什么?

    最佳答案

    (这只是我的推测,但评论太久了)

    这里发生了什么?

    由于构造函数是constexpr,因此可能整个Payload具有一些在编译时计算的值。然后,在运行时,返回完整的Payload。据我所知,编译器不需要认识到编译时值的特定部分是未初始化的,也不应该为其生成任何代码。

    在一些疯狂的编译器中,甚至可能会发生这样的情况,即编译时Payload在未初始化的节中具有垃圾值,然后会产生例如:

    createStorage2():
            mov     rax, rdi
            mov     QWORD PTR [rdi], 0xbaadf00d
            mov     QWORD PTR [rdi+8], 0xbaadf00d
            mov     QWORD PTR [rdi+16], 0xbaadf00d
            mov     QWORD PTR [rdi+24], 0
            ret
    

    通常,constexpr不喜欢未初始化的值,但结合可以稍微解决它。

    关于c++ - 过度渴望使用constexpr进行C++ union 零初始化,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/60568901/

    相关文章:

    c++ - 获取默认初始化(非值/零初始化)的 POD 作为右值

    C++ 枚举与 C# 一样的属性

    c++ - 如何从多个线程使用 ffmpeg 的 sws_scale?

    c++ - 在 CUDA 中交换两个寄存器变量的有效方法是什么?

    c++ - 如何使用相同的成员函数更改相同的不同元素?

    c++ - const 和 constexpr 最终会是一回事吗?

    c++ - 为什么即使成员函数是 constexpr 也需要 constexpr?

    c++ - 指向数据成员转换的 Constexpr 指针