Comparison operators
比较参数。
| 运算符名称 | 语法 | 可重载 | 原型示例(针对 class T ) | |
|---|---|---|---|---|
| 类内定义 | 类外定义 | |||
| 等于 |
a == b
|
是 | bool T :: operator == ( const U & b ) const ; | bool operator == ( const T & a, const U & b ) ; |
| 不等于 |
a != b
|
是 | bool T :: operator ! = ( const U & b ) const ; | bool operator ! = ( const T & a, const U & b ) ; |
| 小于 |
a < b
|
是 | bool T :: operator < ( const U & b ) const ; | bool operator < ( const T & a, const U & b ) ; |
| 大于 |
a > b
|
是 | bool T :: operator > ( const U & b ) const ; | bool operator > ( const T & a, const U & b ) ; |
| 小于等于 |
a <= b
|
是 | bool T :: operator <= ( const U & b ) const ; | bool operator <= ( const T & a, const U & b ) ; |
| 大于等于 |
a >= b
|
是 | bool T :: operator >= ( const U & b ) const ; | bool operator >= ( const T & a, const U & b ) ; |
| 三路比较 (C++20) |
a <=> b
|
是 |
R
T
::
operator
<=>
(
const
U
&
b
)
const
;
[1]
|
R
operator
<=>
(
const
T
&
a,
const
U
&
b
)
;
[1]
|
|
||||
目录 |
双向比较
双向比较运算符表达式的形式为
关系运算符
lhs
<
rhs
|
(1) | ||||||||
lhs
>
rhs
|
(2) | ||||||||
lhs
<=
rhs
|
(3) | ||||||||
lhs
>=
rhs
|
(4) | ||||||||
标签内内容均已完整保留。
相等运算符
lhs
==
rhs
|
(5) | ||||||||
lhs
!=
rhs
|
(6) | ||||||||
`标签内的运算符`==`和`!=`未翻译
- C++术语"lhs"和"rhs"(left-hand side/右手边,right-hand side/左手边)按专业惯例保留不译
- 仅对表格结构进行了呈现,未添加额外翻译内容
内置双向比较运算符
对于内置的双向比较运算符, 左值到右值转换 、 数组到指针转换 (直至 C++26) 以及 函数到指针转换 会被应用于 lhs 和 rhs 。
|
若在应用这些转换前 lhs 和 rhs 均具有数组类型,则该比较操作被弃用。 |
(since C++20)
(until C++26) |
|
对于内置关系运算符,若其中一个操作数是指针,则对另一个操作数执行 数组到指针转换 。 对于内置相等性运算符,若其中一个操作数是指针或 空指针常量 ,则对另一个操作数执行数组到指针转换。 |
(since C++26) |
对于内置的双向比较运算符,其结果为 bool 纯右值。
内置算术比较
如果转换后的两个操作数都具有算术或枚举类型(有作用域或无作用域),则对两个操作数执行 常规算术转换 。转换后的值将进行比较:
#include <iostream> int main() { static_assert(sizeof(unsigned char) < sizeof(int), "Cannot compare signed and smaller unsigned properly"); int a = -1; int b = 1; unsigned int c = 1; unsigned char d = 1; std::cout << std::boolalpha << "Comparing two signed values:\n" " -1 == 1 ? " << (a == b) << "\n" " -1 < 1 ? " << (a < b) << "\n" " -1 > 1 ? " << (a > b) << "\n" "Comparing signed and unsigned:\n" // may issue different-signedness warning: " -1 == 1 ? " << (a == c) << "\n" // may issue different-signedness warning: " -1 < 1 ? " << (a < c) << "\n" // may issue different-signedness warning: " -1 > 1 ? " << (a > c) << "\n" "Comparing signed and smaller unsigned:\n" " -1 == 1 ? " << (a == d) << "\n" " -1 < 1 ? " << (a < d) << "\n" " -1 > 1 ? " << (a > d) << '\n'; }
输出:
Comparing two signed values: -1 == 1 ? false -1 < 1 ? true -1 > 1 ? false Comparing signed and unsigned: -1 == 1 ? false -1 < 1 ? false -1 > 1 ? true Comparing signed and smaller unsigned: -1 == 1 ? false -1 < 1 ? true -1 > 1 ? false
内置指针相等比较
相等运算符
==
和
!=
转换后的操作数也可以具有
类型
std::nullptr_t
,
(C++11 起)
指针类型或成员指针类型。
内置指针相等性比较存在三种可能结果:相等、不相等和未指定。以下列出内置指针相等性比较的等值运算符返回值:
| p 与 q 的比较结果 | 表达式返回值 | |
|---|---|---|
| p == q | p ! = q | |
| 相等 | true | false |
| 不相等 | false | true |
| 未指定 | 未指定的 bool 值 | |
如果转换后的 lhs 和 rhs 中至少有一个是指针,则对两个转换后的操作数执行 指针转换 、 函数指针转换 (C++17 起) 和 限定转换 ,使其达到 复合指针类型 。复合指针类型的两个指针按以下方式进行比较:
- 如果一个指针 表示 完整对象的地址,而另一个指针
-
- 表示超出另一个完整非数组对象末尾的地址,或
- 表示超出另一个完整数组对象最后一个元素末尾的地址,
- 则比较结果未指定。
- 否则,如果两个指针均为空、指向同一函数,或表示同一地址(即它们指向或超越同一对象的末尾),则它们比较相等。
- 否则,指针比较不相等。
如果转换后的 lhs 和 rhs 中至少有一个是指向成员的指针,则会对两个转换后的操作数执行 指向成员指针转换 、 函数指针转换 (C++17 起) 和 限定转换 ,以使它们达到其 复合指针类型 。随后按以下方式比较复合指针类型的两个指向成员的指针:
- 如果两个成员指针都是空成员指针值,则它们比较相等。
- 如果两个成员指针中仅有一个是空成员指针值,则它们比较不相等。
- 如果任意一个是指向 虚成员函数 的指针,则结果未指定。
-
如果一个指向类
C1的成员,另一个指向不同类C2的成员,且这两个类互不为基类,则结果未指定。 - 如果两个指针都指向同一 联合体 的(可能不同的)成员,则它们比较相等。
- 否则,如果通过关联类类型的假设对象进行间接寻址时,两个成员指针会指向同一 最终派生对象 的同一成员或同一子对象,则它们比较相等,否则比较不相等。
struct P {}; struct Q : P { int x; }; struct R : P { int x; }; int P::*bx = (int(P::*)) &Q::x; int P::*cx = (int(P::*)) &R::x; bool b1 = (bx == cx); // 未指定 struct B { int f(); }; struct L : B {}; struct R : B {}; struct D : L, R {}; int (B::*pb)() = &B::f; int (L::*pl)() = pb; int (R::*pr)() = pb; int (D::*pdl)() = pl; int (D::*pdr)() = pr; bool x = (pdl == pdr); // false bool y = (pb == pl); // true
|
两个类型为 std::nullptr_t 的操作数,或一个类型为 std::nullptr_t 的操作数与另一个空指针常量,比较结果相等。 |
(since C++11) |
内置指针关系比较
关系运算符
>
、
<
、
>=
和
<=
转换后的操作数也可以具有指针类型。
对于不相等指针 p 和 q 的内置指针关系比较,存在三种可能结果: p 较大、 q 较大以及未指定。内置指针关系比较中关系运算符所生成的值如下所示:
| p 与 q 的比较结果 | 返回的值 | |||
|---|---|---|---|---|
| p > q | p < q | p >= q | p <= q | |
| 相等 | false | false | true | true |
| p 更大 | true | false | true | false |
| q 更大 | false | true | false | true |
| 未指定 | 未指定的 bool 值 | |||
如果转换后的 lhs 和 rhs 均为指针,则对两个转换后的操作数执行 指针转换 、 函数指针转换 (C++17 起) 和 限定转换 ,使其达到 复合指针类型 。复合指针类型的两个指针按以下方式进行比较:
- 若指针比较相等或相等比较结果未指定,则关系比较结果属于同一类别。
- 否则(指针比较不相等),若任一指针不是指向对象的指针,则结果未指定。
- 否则(两个指针均指向对象),结果将根据符合以下规则的部分顺序进行定义:
-
- 给定数组中的两个不同元素 high 和 low ,其中 high 的下标高于 low ,若一个指针指向 high (或其子对象),另一个指针指向 low (或其子对象),则前者比较结果大于后者。
- 若一个指针指向数组元素 elem (或其子对象),而另一个指针指向同一数组的尾后位置,则尾后指针的比较结果大于另一个指针。
- 若一个指针指向完整对象、基类子对象或成员子对象 obj (或其子对象),而另一个指针指向 obj 的尾后位置,则尾后指针的比较结果大于另一个指针。
-
- 若指针指向同一非联合类类型对象中 非零大小 (C++20 起) 的非静态数据成员 (且成员访问权限相同 (C++23 前) ),或递归地指向这些成员的子对象,则后声明的成员的指针比较结果大于另一指针。
- 否则,结果未指定。
指针全序
在每个程序中存在一个 针对指针的实现定义严格全序 。该严格全序与前述偏序保持一致:未指定的结果变为实现定义,而其他结果保持不变。
指针与严格全序的比较适用于以下情况:
- 调用 operator ( ) 的指针类型特化版本: std::less 、 std::greater 、 std::less_equal 和 std::greater_equal 。
|
(since C++14) |
|
(since C++20) |
重载
在
针对用户定义运算符的重载决议
中,对于每对提升的算术类型
L
和
R
(包括枚举类型),以下函数签名会参与重载决议:
|
bool
operator
<
(
L, R
)
;
|
||
|
bool
operator
>
(
L, R
)
;
|
||
|
bool
operator
<=
(
L, R
)
;
|
||
|
bool
operator
>=
(
L, R
)
;
|
||
|
bool
operator
==
(
L, R
)
;
|
||
|
bool
operator
!
=
(
L, R
)
;
|
||
对于每个类型
P
,若其为对象指针或函数指针,下列函数签名将参与重载决议:
|
bool
operator
<
(
P, P
)
;
|
||
|
bool
operator
>
(
P, P
)
;
|
||
|
bool
operator
<=
(
P, P
)
;
|
||
|
bool
operator
>=
(
P, P
)
;
|
||
|
bool
operator
==
(
P, P
)
;
|
||
|
bool
operator
!
=
(
P, P
)
;
|
||
对于每个作为成员对象指针或成员函数指针
或
std::nullptr_t
(C++11 起)
的类型
MP
,以下函数签名参与重载决议:
|
bool
operator
==
(
MP, MP
)
;
|
||
|
bool
operator
!
=
(
MP, MP
)
;
|
||
#include <iostream> struct Foo { int n1; int n2; }; union Union { int n; double d; }; int main() { std::cout << std::boolalpha; char a[4] = "abc"; char* p1 = &a[1]; char* p2 = &a[2]; std::cout << "指向数组元素的指针:\n" << "p1 == p2? " << (p1 == p2) << '\n' << "p1 < p2? " << (p1 < p2) << '\n'; Foo f; int* p3 = &f.n1; int* p4 = &f.n2; std::cout << "指向类成员的指针:\n" << "p3 == p4? " << (p3 == p4) << '\n' << "p3 < p4? " << (p3 < p4) << '\n'; Union u; int* p5 = &u.n; double* p6 = &u.d; std::cout << "指向联合体成员的指针:\n" << "p5 == (void*)p6? " << (p5 == (void*)p6) << '\n' << "p5 < (void*)p6? " << (p5 < (void*)p6) << '\n'; }
输出:
指向数组元素的指针: p1 == p2? false p1 < p2? true 指向类成员的指针: p3 == p4? false p3 < p4? true 指向联合体成员的指针: p5 == (void*)p6? true p5 < (void*)p6? false
三路比较三路比较运算符表达式的形式为
该表达式返回一个对象,使得
如果其中一个操作数是 bool 类型而另一个不是,则程序非良构。 如果两个操作数都具有算术类型,或者一个操作数具有无作用域枚举类型而另一个具有整型,则对操作数应用常用算术转换,然后
如果两个操作数具有相同的枚举类型
如果至少有一个操作数是指向对象的指针或指向成员的指针,则对两个操作数应用 数组到指针转换 、 指针转换 和 限定转换 ,以使它们达到其 复合指针类型 。 对于转换后的指针操作数 p 和 q , p <=> q 返回类型为 std::strong_ordering 的纯右值:
否则,程序非良构。
重载
在
针对用户定义运算符的重载决议
中,对于指针或枚举类型
其中
运行此代码
#include <compare> #include <iostream> int main() { double foo = -0.0; double bar = 0.0; auto res = foo <=> bar; if (res < 0) std::cout << "-0 is less than 0"; else if (res > 0) std::cout << "-0 is greater than 0"; else if (res == 0) std::cout << "-0 and 0 are equal"; else std::cout << "-0 and 0 are unordered"; } 输出: -0 and 0 are equal |
(since C++20) | |||||||||||||||||||||||||||||
注释
由于比较运算符采用从左到右的分组方式,表达式 a < b < c 会被解析为 ( a < b ) < c ,而非 a < ( b < c ) 或 ( a < b ) && ( b < c ) 。
#include <iostream> int main() { int a = 3, b = 2, c = 1; std::cout << std::boolalpha << (a < b < c) << '\n' // 结果为 true;可能产生警告 << ((a < b) < c) << '\n' // 结果为 true << (a < (b < c)) << '\n' // 结果为 false << ((a < b) && (b < c)) << '\n'; // 结果为 false }
对于 用户自定义的 operator< ,一个常见要求是实现 严格弱序 。特别地,标准算法和容器在处理 Compare 类型时需要满足此要求: std::sort 、 std::max_element 、 std::map 等。
指向同一类不同非静态数据成员的 比较结果 表明,非静态数据成员 在三种 成员访问模式 中 (until C++23) 均按照声明顺序在内存中排列。
尽管比较随机来源的指针(例如并非全部指向同一数组元素)的结果是未指定的,但许多实现提供了指针的 严格全序 关系,例如当指针被实现为连续虚拟地址空间中的地址时。对于那些不提供此类严格全序的实现(例如指针并非所有位都用于表示内存地址而必须在比较时忽略某些位,或需要额外计算,或指针与整数非一一对应关系),它们会提供 std::less 针对指针的特化版本以保障该特性。这使得所有随机来源的指针都能作为键值用于标准关联容器,例如 std::set 或 std::map 。
对于同时满足 可相等比较 和 可小于比较 的类型,C++标准库对 相等性 (即表达式 a == b 的值)与 等价性 (即表达式 ! ( a < b ) && ! ( b < a ) 的值)进行了明确区分。
指针与空指针常量之间的比较已通过包含在 N3624 中的 CWG issue 583 决议被移除:
void f(char* p) { if (p > 0) { /*...*/ } // 使用 N3624 时报错,N3624 前可编译 if (p > nullptr) { /*...*/ } // 使用 N3624 时报错,N3624 前可编译 } int main() {}
三路比较可为类类型自动生成,详见 默认比较 。
如果两个操作数都是数组,三路比较将无法形成。
unsigned int i = 1; auto r = -1 < i; // 现有陷阱:返回‘false’ auto r2 = -1 <=> i; // 错误:需要窄化转换
| 功能测试 宏 | 值 | 标准 | 功能 |
|---|---|---|---|
__cpp_impl_three_way_comparison
|
201907L
|
(C++20) | 三路比较(编译器支持) |
__cpp_lib_three_way_comparison
|
201907L
|
(C++20) | 三路比较(库支持);向库中添加三路比较功能 |
标准库
标准库中的许多类都重载了比较运算符。
|
(C++20 中移除)
|
检查对象是否引用相同类型
(
std::type_info
的公开成员函数)
|
|
(C++20 中移除)
(C++20 中移除)
(C++20)
|
比较两个
error_code
(函数) |
|
(C++20 中移除)
(C++20 中移除)
(C++20)
|
比较
error_condition
与
error_code
(函数) |
|
(C++20 中移除)
(C++20 中移除)
(C++20 中移除)
(C++20 中移除)
(C++20 中移除)
(C++20)
|
按字典序比较
pair
中的值
(函数模板) |
|
(C++20 中移除)
(C++20 中移除)
(C++20 中移除)
(C++20 中移除)
(C++20 中移除)
(C++20)
|
按字典序比较元组中的值
(函数模板) |
|
(C++20 中移除)
|
比较内容
(
std::bitset<N>
的公开成员函数)
|
|
(C++20 中移除)
|
比较两个分配器实例
(
std::allocator<T>
的公开成员函数)
|
|
(C++20 中移除)
(C++20)
|
与另一个
unique_ptr
或与
nullptr
比较
(函数模板) |
|
(C++20 中移除)
(C++20 中移除)
(C++20 中移除)
(C++20 中移除)
(C++20 中移除)
(C++20)
|
与另一个
shared_ptr
或与
nullptr
比较
(函数模板) |
|
(C++20 中移除)
|
比较
std::function
与
nullptr
(函数模板) |
|
(C++11)
(C++11)
(C++20 中移除)
(C++11)
(C++11)
(C++11)
(C++11)
(C++20)
|
比较两个时长
(函数模板) |
|
(C++11)
(C++11)
(C++20 中移除)
(C++11)
(C++11)
(C++11)
(C++11)
(C++20)
|
比较两个时间点
(函数模板) |
命名空间 std::rel_ops 提供通用运算符 ! = 、 > 、 <= 和 >= :
|
定义于头文件
<utility>
|
|
|
定义于命名空间
std::rel_ops
|
|
|
(C++20 中弃用)
|
基于用户定义的
operator
==
和
operator
<
自动生成比较运算符
(函数模板) |
缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的C++标准。
| 缺陷报告 | 适用版本 | 发布时行为 | 正确行为 |
|---|---|---|---|
|
CWG 583
( N3624 ) |
C++98 |
所有六个比较运算符都可用于
比较指针与空指针常量 |
仅允许相等性运算符 |
| CWG 661 | C++98 |
算术比较的实际语义(例如
1 < 2 返回 true 还是 false )未作规定 |
已添加规范说明 |
| CWG 879 | C++98 |
指向函数类型的指针和指向
void 的指针没有内置比较功能 |
为这些指针添加了
比较规范 |
| CWG 1596 | C++98 |
非数组对象仅在指针算术运算时
被视为属于单元素数组 |
该规则也
适用于比较运算 |
| CWG 1598 | C++98 |
指向不同类成员且这两个类
互不为基类的两个成员指针即使 所指成员的偏移量可能相同也不相等 |
此种情况下
结果未作规定 |
| CWG 1858 | C++98 |
未明确指向同一联合体不同成员
的两个成员指针是否应如同 指向同一成员般比较相等 |
此种情况下
它们比较相等 |
| CWG 2419 | C++98 |
仅当通过
&
获取指针时,指向非数组对象的指针
在指针比较中才被视为指向大小为1的数组首元素 |
适用于所有指向
非数组对象的指针 |
| CWG 2526 | C++98 |
指向
void
和函数指针的关系比较定义(
>
,
>=
,
<
和
<=
)
被 N3624 移除 |
已恢复 |
| CWG 2796 | C++17 |
在内置指针关系比较期间未对转换后的
指针操作数执行函数指针转换 |
在此情况下执行
这些转换 |
参见
| 常用运算符 | ||||||
|---|---|---|---|---|---|---|
| 赋值 |
自增
自减 |
算术 | 逻辑 | 比较 |
成员
访问 |
其他 |
|
a
=
b
|
++
a
|
+
a
|
!
a
|
a
==
b
|
a
[
...
]
|
函数调用
a ( ... ) |
|
逗号
a, b |
||||||
|
条件
a ? b : c |
||||||
| 特殊运算符 | ||||||
|
static_cast
将一种类型转换为另一种相关类型
|
||||||
|
C 文档
关于
比较运算符
|