C++20概念boolean-testable之谜

The mystery of C++20 concept boolean-testable

C++20引入了一个比较概念boolean-testable,但是我注意到它的斜体和中间的连字符,说明它只是为了说明,而且由于没有所谓的std::boolean_testable in <concepts>,我们不能在自己的代码中使用它。

这个仅展示概念的目的是什么?为什么这个概念如此神秘?

与所有仅供说明的概念一样,其目的是简化标准中的规范。它只是一个构建块,用于指定其他(可能面向用户的)概念,而无需重复概念模型的内容。值得注意的是,它出现在 another exposition-only concept

的规范中
template<class T, class U>
  concept weakly-equality-comparable-with = // exposition only
    requires(const remove_reference_t<T>& t,
             const remove_reference_t<U>& u) {
      { t == u } -> boolean-testable;
      { t != u } -> boolean-testable;
      { u == t } -> boolean-testable;
      { u != t } -> boolean-testable;
    };

weakly-equality-comparable-with 满足使用 return 类型重载比较运算符的类型,而 bool 不一定是逐字的。我们仍然可以使用这些表达式来比较对象,因此该标准试图对它们进行推理。而且这不是假设,它们可以出现在野外。来自 Palo Alto report 的示例:

... One such example is an early version of the QChar class (1.5 and earlier, at least) (Nokia Corporation, 2011).

class QChar
{
  friend int operator==(QChar c1, QChar c2);
  friend int operator!=(QChar c1, QChar c2);
};

We should be able to use this class in our standard algorithms, despite the fact that the operator does not return a bool.

关于你的另一个问题

And why is this concept so mysterious?

不是。但是,如果仅在 cppreference 上检查它,可能会错过上下文,因为在那里交叉引用它可能并不容易。

boolean-testable 来自 LWG 反复尝试准确指定类型何时足以“boolean-ish”以适合作为结果使用谓词和比较。

起初,公式只是“可转换为 bool”,在 C++11 中“可根据上下文转换为 bool”,但 LWG2114 指出这是不够的: 如果对某物的唯一要求是它可以转换为 bool,那么你唯一能做的就是将它转换为 bool。您不能写 !pred(x)i != last && pred(*i),因为 !&& 可能会超载以执行任何操作。这将需要到处乱扔代码并显式 bool 转换。

库的真正含义是“它会在我们需要时转换为 bool”,但事实证明这真的难以表达:我们想要b1 && b2 使用 built-in 运算符 && 的 short-circuiting 魔法,即使 b1b2 是不同的“boolean-ish”类型.但是当单独查看 b1 的类型时,我们不知道还有什么其他的“boolean-ish”类型。孤立地分析类型基本上是不可能的。

然后 Ranges TS 出现了,并尝试 specify a Boolean concept。这是一个非常复杂的概念 - 有十几个表达式要求 - 仍然无法解决 mixed-type 比较问题,并增加了自己的问题。例如,它要求 bool(b1 == b2) 等于 bool(b1) == bool(b2)bool(b1 == bool(b2)),这意味着 int 不对 Boolean 建模,除非它被限制在域中{0, 1}.

随着 C++20 即将发布,这些问题导致 P1934R0 to propose throwing in the towel: just require the type to model convertible_to<bool>, and require users to cast it to bool when needed. That came with its own problems, as P1964R0 指出,尤其是现在我们正在发布供 public 消费的概念:我们真的要强迫用户乱扔垃圾吗?使用强制转换为 bool 的标准库概念限制的代码?特别是如果只有一小部分用户使用过载 &&|| 的病态类型,并且没有标准库实现支持此类类型?

最后的结果是boolean-testable,这样设计是为了保证你可以使用!(只是一次——!!x不是需要工作),&&|| 在 predicate/comparison 的结果上并获得正常语义(包括 short-circuiting 用于 &&|| ).为了解决 mixed-type 问题,它的规范包含一大堆复杂的标准术语,谈论名称查找和模板参数推导以及隐式转换序列,但它实际上归结为“不要愚蠢”。 P1964R2 包括详细的措辞原理。

为什么是exposition-only? boolean-testable 在 C++20 周期中来得很晚:LEWG 周五下午在贝尔法斯特批准了 P1964 的方向(2019 年 11 月的会议,C++20 发布前的一次会议) ,并且拥有 exposition-only 概念的风险比命名概念的风险要低得多,尤其是因为也没有太多的动机去实现它 public。当然,LEWG 会议室中没有人要求为其命名。

您可以使用std::_Boolean_testable<T>