作者对 optional<bool> 的基于联合的实现是否在 P2641 中定义得当?

Is the author's union-based implementation of an optional<bool> well-defined in P2641?

提问人:Jan Schultke 提问时间:9/23/2023 最后编辑:timrauJan Schultke 更新时间:10/1/2023 访问量:160

问:

P2641r4: Checking a union alternative is active 中,作者提供了一个 的实现作为激励示例,并声称这是正确的格式。optional<bool>

struct OptBool {
  union { bool b; char c; };

  OptBool() : c(2) { }
  OptBool(bool b) : b(b) { }

  auto has_value() const -> bool {
    return c != 2;
  }

  auto operator*() -> bool& {
    return b;
  }
};

但是,我不相信。也就是说,看起来并不安全,因为如果 a 是活跃的工会成员,那么就会访问一个不活跃的成员并执行工会类型的双关语。据我所知,这在 C++ 中是不允许的。has_value()boolc != 2

作者解释说,由于正在读取非活动的联合成员,因此无法执行此操作,并提供了以下实现:

  constexpr auto has_value() const -> bool {
    if consteval {
      return std::is_within_lifetime(&b);
    } else {
      return c != 2;
    }
  }

作者这是什么意思?这是否意味着您不能在常量表达式中读取非活动的联合成员,但否则是允许的? 此代码的格式是否完全正确,或者它是否依赖于允许在运行时使用联合类型双关语的编译器扩展?


注意:这是作者对 P2641 中明确定义的可选<bool>的实现的姊妹问题,该问题讨论了其他实现。

C++ 语言-Lawyer 联合 类型双关语 C++26

评论

1赞 Quimby 9/23/2023
联盟似乎根本没有必要,人们可以单独存储三态。bool
1赞 Jan Schultke 9/23/2023
@Quimby作者的想法是,这是 for 的专用化,所以它仍然需要有一个可选类型的接口。optional<T>bool
0赞 ALX23z 9/23/2023
IIRC C++ 不允许这样做。但我不相信有一个有效的 C++ 编译器不会接受它或出现故障。
1赞 Red.Wave 9/23/2023
当我将一个问题标记为语言律师时,我希望巴里(那篇文章的作者)出现。律师使用非常合法的文献来描述事物。如果您不理解这篇文章,那是因为它所解决的问题的级别。简而言之,这两种实现都包含某种程度的 UB,或者违反了严格的别名规则。作者正在利用这个例子来开始讨论一个 conexpr 改进提案。否则,将是一个很好的实现候选者:它提供了所需的功能,但需要监管。boost::tribooloptional<bool>
0赞 YSC 9/23/2023
下面解释如下:“这两者都很好:operator* 有效,因为我们在这两种情况下都返回了对非常活跃的 bool 的引用,而 has_value() 有效,因为我们被允许读取 char(明确地,根据 [basic.lval]/11.31)。

答:

1赞 user17732522 9/23/2023 #1

我假设它像往常一样有一个先决条件,即已使用重载。当可选为空时使用显然是UB,但也不是预期用途。operator*OptBool(bool b)operator*

When 是活动成员,则访问 具有未定义的行为,因为它必须超出生命周期。bc

这里的目的是查看对象表示,这可以通过添加看似不必要的强制转换来实现:

return *reinterpret_cast<unsigned char*>(reinterpret_cast<OptBool*>(&c)) != 2;

内部转换将生成指向对象的指针,因为该指针是标准布局,并且指针可与子对象互换。OptBoolOptBoolc

然后,外部强制转换将生成指向具有表达式类型 的对象的指针。通过它进行访问不是别名冲突。但是,目前尚未指定此访问应读取的值。目的是让它读取对象(以及 or 对象)的对象表示的第一个字节,但目前没有指定要发生这种情况。有 P1839 试图解决这个问题。在实践中,这是每个人都认为的行为,即使标准目前没有这么说,这也是一个缺陷。OptBoolunsigned char*OptBoolboolchar

无论如何,实现当然假定了 的特定实现,特别是它的大小、对齐方式和对象/值表示。bool

0赞 Daniel 9/23/2023 #2

标准中没有任何内容要求或不存储为值 2。
要求为 0 并且要求为 1,但这些是强制转换的结果,而不是存储在内存中的值。
查看 https://stackoverflow.com/a/19351548/362589
falsetrueint(false)int(true)

评论

0赞 Jan Schultke 9/23/2023
这并不能完全回答这个问题。它只是意味着,如果代码格式良好,它仍然会受到对平台 ABI 和编译器的假设的影响。但是,它首先没有回答代码的格式是否正确。