当我试图学习c++操作符时,我偶然发现了下面的表格,其中列出了一个奇怪的比较操作符。<=>操作符有什么作用?

自2017年以来,cppreference.com更新了该页面,现在包含关于<=>操作符的详细信息。


当前回答

默认<=>自动提供==,!=,<,>,<=,>=

c++ 20有一个新的“默认比较”特性设置,因此默认<=>将免费提供所有其他的比较。我相信这是添加运算符<=>的主要动机。

改编自https://en.cppreference.com/w/cpp/language/default_comparisons:

main.cpp

#include <cassert>
#include <compare>
#include <set>

struct Point {
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
};

int main() {
    Point pt1{1, 1}, pt2{1, 2};

    // Just to show it Is enough for `std::set`.
    std::set<Point> s;
    s.insert(pt1);

    // All of these are automatically defined for us!
    assert(!(pt1 == pt2));
    assert( (pt1 != pt2));
    assert( (pt1 <  pt2));
    assert( (pt1 <= pt2));
    assert(!(pt1 >  pt2));
    assert(!(pt1 >= pt2));
}

编译并运行:

sudo apt install g++-10
g++-10 -ggdb3 -O0 -std=c++20 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

上述内容的一个等效的更明确的版本是:

struct Point {
    int x;
    int y;
    auto operator<=>(const Point& other) const {
        if (x < other.x) return -1;
        if (x > other.x) return 1;
        if (y < other.y) return -1;
        if (y > other.y) return 1;
        return 0;
    }
    bool operator==(const Point& other) const = default;
};

在这种情况下,我们需要显式地设置bool operator==(const Point& other) const = default;因为如果operator<=>没有默认值(例如上面明确给出的),那么operator==不会自动默认值:

根据任何运算符<=>重载的规则,默认的<=>重载也将允许该类型与<、<=、>和>=进行比较。 如果operator<=>是默认值,而operator==完全没有声明,则operator==是隐式默认值。

上面的例子使用了与默认操作符<=>相同的算法,正如cppreference解释的那样:

默认操作符<=>执行字典比较,先后比较T的基本子对象(从左到右深度优先)和非静态成员子对象(按声明顺序)来计算<=>,递归地展开数组成员(按下标增加的顺序),并在发现不相等的结果时提前停止

在c++ 20之前,你不能做像operator== = default这样的事情,并且定义一个操作符不会导致其他操作符被定义,例如使用-std=c++17编译失败:

#include <cassert>

struct Point {
    int x;
    int y;
    auto operator==(const Point& other) const {
        return x == other.x && y == other.y;
    };
};

int main() {
    Point pt1{1, 1}, pt2{1, 2};

    // Do some checks.
    assert(!(pt1 == pt2));
    assert( (pt1 != pt2));
}

错误:

main.cpp:16:18: error: no match for ‘operator!=’ (operand types are ‘Point’ and ‘Point’)
   16 |     assert( (pt1 != pt2));
      |              ~~~ ^~ ~~~
      |              |      |
      |              Point  Point

然而,上面的代码在-std=c++20下编译。

相关:任何c++操作符重载是基于其他操作符自动提供的吗?

在Ubuntu 20.04、GCC 10.2.0上测试。

其他回答

在2017-11-11,ISO c++委员会采纳了Herb Sutter提出的<=> "太空船"三向比较运算符,作为c++ 20的新特性之一。在题为一致比较Sutter, Maurer和Brown的论文中演示了新设计的概念。关于该提案的概述,以下是文章的摘录:

The expression a <=> b returns an object that compares <0 if a < b, compares >0 if a > b, and compares ==0 if a and b are equal/equivalent. Common case: To write all comparisons for your type X with type Y, with memberwise semantics, just write: auto X::operator<=>(const Y&) =default; Advanced cases: To write all comparisons for your type X with type Y, just write operator<=> that takes a Y, can use =default to get memberwise semantics if desired, and returns the appropriate category type: Return an _ordering if your type naturally supports <, and we’ll efficiently generate symmetric <, >, <=, >=, ==, and !=; otherwise return an _equality, and we’ll efficiently generate symmetric == and !=. Return strong_ if for your type a == b implies f(a) == f(b) (substitutability, where f reads only comparison-salient state that is accessible using the public const members), otherwise return weak_.

Comparison Categories

五个比较类别被定义为std:: types, 使用以下预定义值:

┌──────────────────┬───────────────────────────────────┬─────────────┐
│                  │          Numeric  values          │ Non-numeric │
│     Category     ├──────┬────────────┬───────────────┤             │
│                  │ -1   │ 0          │ +1            │   values    │
├──────────────────┼──────┼────────────┼───────────────┼─────────────┤
│ strong_ordering  │ less │ equal      │ greater       │             │
│ weak_ordering    │ less │ equivalent │ greater       │             │
│ partial_ordering │ less │ equivalent │ greater       │ unordered   │
│ strong_equality  │      │ equal      │ nonequal      │             │
│ weak_equality    │      │ equivalent │ nonequivalent │             │
└──────────────────┴──────┴────────────┴───────────────┴─────────────┘

这些类型之间的隐式转换定义如下:

strong_ordering with values {less, equal, greater} implicitly converts to: weak_ordering with values {less, equivalent, greater} partial_ordering with values {less, equivalent, greater} strong_equality with values {unequal, equal, unequal} weak_equality with values {nonequivalent, equivalent, nonequivalent} weak_ordering with values {less, equivalent, greater} implicitly converts to: partial_ordering with values {less, equivalent, greater} weak_equality with values {nonequivalent, equivalent, nonequivalent} partial_ordering with values {less, equivalent, greater, unordered} implicitly converts to: weak_equality with values {nonequivalent, equivalent, nonequivalent, nonequivalent} strong_equality with values {equal, unequal} implicitly converts to: weak_equality with values {equivalent, nonequivalent}

Three-way comparison

引入了<=>令牌。在旧源代码中,字符序列<=>标记为<=>。例如,X<&Y::operator<=>需要添加一个空格来保留其含义。

可重载操作符<=>是一个三向比较函数,其优先级高于<,低于<<。它返回一个可以与literal0比较的类型,但也允许返回其他类型,例如支持表达式模板。在语言和标准库中定义的所有<=>运算符都会返回前面提到的5个std::比较类别类型之一。

对于语言类型,提供了以下内置的<=>同类型比较。所有都是constexpr,除非另有说明。不能使用标量提升/转换异构地调用这些比较。

Forbool, integral, and pointer types,<=>returnsstrong_ordering. For pointer types, the different cv-qualifications and derived-to-base conversions are allowed to invoke a homogeneous built-in<=>, and there are built-in heterogeneousoperator<=>(T*, nullptr_t). Only comparisons of pointers to the same object/allocation are constant expressions. For fundamental floating point types,<=> returnspartial_ordering, and can be invoked heterogeneously by widening arguments to a larger floating point type. For enumerations,<=> returns the same as the enumeration's underlying type's<=>. Fornullptr_t,<=> returnsstrong_orderingand always yieldsequal. For copyable arrays,T[N] <=> T[N]returns the same type asT's<=>and performs lexicographical elementwise comparison. There is no<=>for other arrays. Forvoidthere is no<=>.

为了更好地理解这个运算符的内部工作原理,请阅读原文。这是我用搜索引擎发现的。

这叫做三向比较运算符。

根据P0515论文提案:

There’s a new three-way comparison operator, <=>. The expression a <=> b returns an object that compares <0 if a < b, compares >0 if a > b, and compares ==0 if a and b are equal/equivalent. To write all comparisons for your type, just write operator<=> that returns the appropriate category type: Return an _ordering if your type naturally supports <, and we’ll efficiently generate <, >, <=, >=, ==, and !=; otherwise return an _equality, and we’ll efficiently generate == and !=. Return strong if for your type a == b implies f(a) == f(b) (substitutability, where f reads only comparison-salient state accessible using the nonprivate const interface), otherwise return weak.

cppreference表示:

三向比较运算符表达式的形式为 LHS <=> RHS (1) 表达式返回一个对象 如果LHS < RHS,则比较<0 LHS > RHS比较>0 如果LHS和RHS相等/等效,则比较==0。

这个答案已经变得无关紧要,因为引用的网页已经改变了

您正在引用的网页已损坏。那天它被编辑了很多次,不同的部分不同步。我当时看到的状态是:

在页面的顶部,它列出了当前存在的比较操作符(在c++ 14中)。这里没有<=>。

在页面的底部,他们应该列出相同的操作符,但他们做了个蠢事,添加了这个未来的建议。

gcc还不知道<=>(和-std=c++14,永远不会),所以它认为你的意思是<=> b。这解释了错误消息。

如果你在五年后尝试同样的事情,你可能会得到一个更好的错误消息,比如<=>不是c++ 14的一部分。

<=>的优点是用于复杂类型,其比较代价很高,例如树导航。你可以“int x = A <=> B;”一次,然后从x中确定>,<,>=,<=,==,!=,而不需要对A和B进行额外的比较。对于树,想象bool find(根,A){如果根为nullptr返回false;int x = A <=> root->B;If !x,你发现它返回真,否则如果x < 0,找到左,否则找到右}。(对尾递归进行调优。)

我以为有些语言有一个三向控制流,像开关,但很难通过谷歌回忆。这种情况可以追溯到C的strcmp(), memcmp(),以及JAVA的compareTo()。众包我!

默认<=>自动提供==,!=,<,>,<=,>=

c++ 20有一个新的“默认比较”特性设置,因此默认<=>将免费提供所有其他的比较。我相信这是添加运算符<=>的主要动机。

改编自https://en.cppreference.com/w/cpp/language/default_comparisons:

main.cpp

#include <cassert>
#include <compare>
#include <set>

struct Point {
    int x;
    int y;
    auto operator<=>(const Point&) const = default;
};

int main() {
    Point pt1{1, 1}, pt2{1, 2};

    // Just to show it Is enough for `std::set`.
    std::set<Point> s;
    s.insert(pt1);

    // All of these are automatically defined for us!
    assert(!(pt1 == pt2));
    assert( (pt1 != pt2));
    assert( (pt1 <  pt2));
    assert( (pt1 <= pt2));
    assert(!(pt1 >  pt2));
    assert(!(pt1 >= pt2));
}

编译并运行:

sudo apt install g++-10
g++-10 -ggdb3 -O0 -std=c++20 -Wall -Wextra -pedantic -o main.out main.cpp
./main.out

上述内容的一个等效的更明确的版本是:

struct Point {
    int x;
    int y;
    auto operator<=>(const Point& other) const {
        if (x < other.x) return -1;
        if (x > other.x) return 1;
        if (y < other.y) return -1;
        if (y > other.y) return 1;
        return 0;
    }
    bool operator==(const Point& other) const = default;
};

在这种情况下,我们需要显式地设置bool operator==(const Point& other) const = default;因为如果operator<=>没有默认值(例如上面明确给出的),那么operator==不会自动默认值:

根据任何运算符<=>重载的规则,默认的<=>重载也将允许该类型与<、<=、>和>=进行比较。 如果operator<=>是默认值,而operator==完全没有声明,则operator==是隐式默认值。

上面的例子使用了与默认操作符<=>相同的算法,正如cppreference解释的那样:

默认操作符<=>执行字典比较,先后比较T的基本子对象(从左到右深度优先)和非静态成员子对象(按声明顺序)来计算<=>,递归地展开数组成员(按下标增加的顺序),并在发现不相等的结果时提前停止

在c++ 20之前,你不能做像operator== = default这样的事情,并且定义一个操作符不会导致其他操作符被定义,例如使用-std=c++17编译失败:

#include <cassert>

struct Point {
    int x;
    int y;
    auto operator==(const Point& other) const {
        return x == other.x && y == other.y;
    };
};

int main() {
    Point pt1{1, 1}, pt2{1, 2};

    // Do some checks.
    assert(!(pt1 == pt2));
    assert( (pt1 != pt2));
}

错误:

main.cpp:16:18: error: no match for ‘operator!=’ (operand types are ‘Point’ and ‘Point’)
   16 |     assert( (pt1 != pt2));
      |              ~~~ ^~ ~~~
      |              |      |
      |              Point  Point

然而,上面的代码在-std=c++20下编译。

相关:任何c++操作符重载是基于其他操作符自动提供的吗?

在Ubuntu 20.04、GCC 10.2.0上测试。