在常量表达式中访问联合成员时出错

AJ *_*Tan 5 c++ unions constexpr

当我遇到问题时,我正在用工会做一些实验。

union U
{
  // struct flag for reverse-initialization of each byte
  struct rinit_t { };
  constexpr static const rinit_t rinit{};

  uint32_t dword;
  uint8_t byte[4];

  constexpr U() noexcept : dword{} { }

  constexpr U(uint32_t x) noexcept : dword{x} { }

  constexpr U(uint32_t x, const rinit_t&) noexcept : dword{}
  {
    U temp{x};
    byte[0] = temp.byte[3];
    byte[1] = temp.byte[2];
    byte[2] = temp.byte[1];
    byte[3] = temp.byte[0];
  }
};
Run Code Online (Sandbox Code Playgroud)

这是我的示例实例:

constexpr U x{0x12345678, U::rinit};
Run Code Online (Sandbox Code Playgroud)

我在 g++ 的 5.1 和 8.1 版中遇到了这个错误-std=c++14-std=c++17并且-std=c++2a

accessing 'U::byte' member instead of initialized 'U::dword' member in constant expression
Run Code Online (Sandbox Code Playgroud)

访问和分配 member 的元素byte,无论是 fromtemp还是this,都会重现错误。似乎byte是由编译器为“未初始化”的成员,即使认可bytedword共享相同的地址。

我曾经修改过第二个构造函数:

accessing 'U::byte' member instead of initialized 'U::dword' member in constant expression
Run Code Online (Sandbox Code Playgroud)

但是我在最终产生似乎是编译器错误后恢复了:

main.cpp:73:37: internal compiler error: in complete_ctor_at_level_p, at expr.c:5844
   constexpr U x{0x12345678, U::rinit};
                                     ^

Please submit a full bug report,
with preprocessed source if appropriate.
See <http://tdm-gcc.tdragon.net/bugs> for instructions.
Run Code Online (Sandbox Code Playgroud)

对于我当前的修复,我添加了一个转换器:

constexpr U(uint32_t x) noexcept :
  byte{uint8_t(x), uint8_t(x >> 8), uint8_t(x >> 16), uint8_t(x >> 24)}
{ }
Run Code Online (Sandbox Code Playgroud)

我修改了第三个构造函数:

main.cpp:73:37: internal compiler error: in complete_ctor_at_level_p, at expr.c:5844
   constexpr U x{0x12345678, U::rinit};
                                     ^

Please submit a full bug report,
with preprocessed source if appropriate.
See <http://tdm-gcc.tdragon.net/bugs> for instructions.
Run Code Online (Sandbox Code Playgroud)

我只是好奇为什么我会收到错误。谁能帮我理解这个问题?

更新:

根据我最近的测试,在constexpr union构造函数中,我不能使用不在初始化列表中的非静态数据成员。因此,我添加了一些struct标志来明确指定某个非静态数据成员的初始化。

// converts the value of a uint32_t to big endian format
constexpr static uint32_t uint32_to_be(uint32_t x)
{
  return ( (x >> 24) & 0xFF)       |
         ( (x << 8)  & 0xFF0000)   |
         ( (x >> 8)  & 0xFF00)     |
         ( (x << 24) & 0xFF000000);
}
Run Code Online (Sandbox Code Playgroud)

有了它,我还为这种初始化添加了新的构造函数。这里有些例子:

constexpr U(uint32_t x, const rinit_t&) noexcept : dword{uint32_to_be(x)} { }
Run Code Online (Sandbox Code Playgroud)

如果有人可以为此提供更好的解决方案,那就太好了。

cig*_*ien 1

你的constexpr函数在c++20之前无效,因为它违反了以下规则

表达式 e 是核心常量表达式,除非对 e 的求值遵循抽象机的规则,将求值以下表达式之一:

赋值表达式或赋值运算符 ([class.copy]) 的调用将更改联合的活动成员;

从 c++20 开始,此限制已在此处澄清:

表达式 e 是核心常量表达式,除非对 e 的求值遵循抽象机的规则,将求值以下表达式之一:

为联合体调用隐式定义的复制/移动构造函数或复制/移动赋值运算符,其活动成员(如果有)是可变的,除非联合对象的生命周期在 E 的求值期间开始;

我相信这使您的代码有效。