-1

动机:有时我使用 std::variant 来实现“花式”枚举,其中一些枚举状态可以携带状态。

现在,如果我想<=>为我的变体使用它,它需要我的空结构已定义 <=>。这对我来说似乎有点奇怪,因为如果类型的状态位为 0,则该类型的所有实例都是相同的。

完整示例

#include <compare>
#include <iostream>
#include <variant>

struct Off{
    // without this the code does not compile
    auto operator<=>(const Off& other) const = default;
};

struct Running{
    int rpm=1000;
    auto operator<=>(const Running& other) const = default;
};

using EngineState = std::variant<Off, Running>;

int main()
{
    EngineState es1, es2;
    es1<=>es2;
}
4

2 回答 2

5

默认的比较运算符是选择加入,而不是选择退出。

如果它是选择退出的,那么它可以将无法编译的代码转换为具有某种未知含义的编译代码。标准委员会试图尽可能地保持向后兼容性。

于 2021-02-26T15:27:59.077 回答
2

其原因最终归结为:“可比较”类型意味着什么?

可比较的类型首先是带有的类型。这种类型的对象带有一个在某种意义上有意义的值。

你的例子就是这样一种情况。本质上,您正在使用variant并使用替代值Off来增加值状态。Running我会用optional<Running>自己,但无论如何。Off是一种在您的问题空间中合法具有价值的类型。它的价值是“不运行”。

大多数空对象类型并非如此。std::in_place_t根据该术语的任何定义,它没有有意义的“价值”。它是一个标签,用于将就地构造调用分派给某些类型的构造函数。要求将一个实例与另一个实例进行比较是没有意义的。

但是,您希望在默认情况下使其具有可比性。

很多无状态类型没有以面向值的方式使用。用于调度重载集的标签、用于提供对全局资源的基于对象的访问的类型等。这并不是说没有面向值的空类型。但是它们太多了,单方面宣布它们都应该默认具有可比性似乎很奇怪。

此外,以价值为导向是可比性的必要条件,但它并不是充分的。对于明显以价值为导向的类型,我们没有默认的可比性。

聚合类型具有所有公共成员,因此没有什么能阻止任何人随时在其中插入任何值。他们显然是以价值为导向的。

然而聚合类型在默认情况下是不可比较的。他们也不应该。并非所有可以假设比较的东西都应该进行比较。

即使可以对特定的值集合进行排序,允许对它们进行排序是否有意义?这取决于收藏。如果我有 2D 点类型,可以订购。但应该吗?我可以理解平等测试,但全面的可比性?你可以定义它,但这样的比较有意义吗?

这是只有类型的创建者才能回答的问题。因此,我们强迫类型的创建者询问他们想要什么。

没有成员只是有一些公共成员的特例。

于 2021-02-27T00:26:33.583 回答