比较运算符
比较参数。
运算符名称 | 语法 | 可重载 | 原型示例 (对于 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++26 前) 和 函数到指针转换 应用于 lhs 和 rhs。
如果 lhs 和 rhs 在应用这些转换之前都具有数组类型,则比较已被弃用。 |
(自 C++20 起) (C++26 前) |
对于内建关系运算符,如果其中一个操作数是指针,则对另一个操作数执行 数组到指针转换。 对于内建相等运算符,如果其中一个操作数是指针或空指针常量,则对另一个操作数执行数组到指针转换。 |
(自 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); // unspecified 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 的操作数和另一个空指针常量,比较相等。 |
(自 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(或 high 的子对象),而另一个指针指向 low(或 low 的子对象),则前者比较大于后者。
- 如果一个指针指向数组的元素 elem(或 elem 的子对象),而另一个指针位于同一数组的末尾之后,则末尾后指针比较大于另一个指针。
- 如果一个指针指向完整对象、基类子对象或成员子对象 obj(或 obj 的子对象),而另一个指针位于 obj 的末尾之后,则末尾后指针比较大于另一个指针。
[编辑] 指针全序
每个程序中都存在一个实现定义的指针严格全序。严格全序与上述部分顺序一致:未指定的结果变为实现定义的,而其他结果保持不变。
在以下情况下应用具有严格全序的指针比较
- 调用 std::less、std::greater、std::less_equal 和 std::greater_equal 的指针类型特化的 operator()。
|
(自 C++14 起) |
|
(自 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); |
||
对于每种类型 MP
,它是指向成员对象的指针或指向成员函数的指针 或 std::nullptr_t(自 C++11 起),以下函数签名参与重载决议
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 << "Pointers to array elements:\n" << "p1 == p2? " << (p1 == p2) << '\n' << "p1 < p2? " << (p1 < p2) << '\n'; Foo f; int* p3 = &f.n1; int* p4 = &f.n2; std::cout << "Pointers to members of a class:\n" << "p3 == p4? " << (p3 == p4) << '\n' << "p3 < p4? " << (p3 < p4) << '\n'; Union u; int* p5 = &u.n; double* p6 = &u.d; std::cout << "Pointers to members of a union:\n" << "p5 == (void*)p6? " << (p5 == (void*)p6) << '\n' << "p5 < (void*)p6? " << (p5 < (void*)p6) << '\n'; }
输出
Pointers to array elements: p1 == p2? false p1 < p2? true Pointers to members of a class: p3 == p4? false p3 < p4? true Pointers to members of a union: 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 |
(自 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; maybe warning << ((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 等。
指向同一类的不同非静态数据成员的指针的比较结果暗示非静态数据成员在三种成员访问模式的每一种中(直到 C++23) 都按照声明顺序在内存中排列。
尽管比较随机来源的指针(例如,并非都指向同一数组的成员)的结果未指定,但许多实现提供了指针的 严格全序,例如,如果它们被实现为连续虚拟地址空间内的地址。那些不这样做的实现(例如,并非指针的所有位都是内存地址的一部分并且必须忽略比较,或者需要额外的计算,或者指针和整数不是 1 对 1 的关系),提供了 std::less 的指针特化,它具有该保证。这使得可以将所有随机来源的指针用作标准关联容器(例如 std::set 或 std::map)中的键。
对于既是 EqualityComparable 又是 LessThanComparable 的类型,C++ 标准库区分了相等性,即表达式 a == b 的值,以及等价性,即表达式 !(a < b) && !(b < a) 的值。
指针和空指针常量之间的比较已通过 CWG 问题 583 的决议删除,该决议包含在 N3624 中
void f(char* p) { if (p > 0) { /*...*/ } // Error with N3624, compiled before N3624 if (p > nullptr) { /*...*/ } // Error with N3624, compiled before N3624 } int main() {}
三路比较可以为类类型自动生成,请参阅默认比较。
如果两个操作数都是数组,则三路比较是非良构的。
unsigned int i = 1; auto r = -1 < i; // existing pitfall: returns ‘false’ auto r2 = -1 <=> i; // Error: narrowing conversion required
特性测试宏 | 值 | Std | 特性 |
---|---|---|---|
__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 起) |
按字典顺序比较 tuple 中的值 (函数模板) |
(在 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 起) |
比较两个 duration (函数模板) |
(C++11 起)(C++11 起)(在 C++20 中移除)(C++11 起)(C++11 起)(C++11 起)(C++11 起)(C++20 起) |
比较两个 time point (函数模板) |
(在 C++20 中移除) |
比较两个 scoped_allocator_adaptor 对象(函数模板) |
(在 C++20 中移除)(C++20 起) |
比较底层的 std::type_info 对象 ( std::type_index 的公共成员函数) |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20 起) |
按字典顺序比较两个字符串 (函数模板) |
(在 C++20 中移除) |
locale 对象之间的相等性比较 ( std::locale 的公共成员函数) |
(C++11 起)(C++11 起)(在 C++20 中移除)(C++11 起)(在 C++20 中移除)(C++11 起)(在 C++20 中移除)(C++11 起)(在 C++20 中移除)(C++11 起)(在 C++20 中移除)(C++20 起) |
按字典顺序比较两个 array 的值(函数模板) |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20 起) |
按字典顺序比较两个 deque 的值(函数模板) |
(C++11 起)(C++11 起)(在 C++20 中移除)(C++11 起)(在 C++20 中移除)(C++11 起)(在 C++20 中移除)(C++11 起)(在 C++20 中移除)(C++11 起)(在 C++20 中移除)(C++20 起) |
按字典顺序比较两个 forward_list 的值(函数模板) |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20 起) |
按字典顺序比较两个 list 的值(函数模板) |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20 起) |
按字典顺序比较两个 vector 的值(函数模板) |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20 起) |
按字典顺序比较两个 map 的值(函数模板) |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20 起) |
按字典顺序比较两个 multimap 的值(函数模板) |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20 起) |
按字典顺序比较两个 set 的值(函数模板) |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20 起) |
按字典顺序比较两个 multiset 的值(函数模板) |
(C++11 起)(C++11 起)(在 C++20 中移除) |
比较 unordered_map 中的值 (函数模板) |
(C++11 起)(C++11 起)(在 C++20 中移除) |
比较 unordered_multimap 中的值 (函数模板) |
(C++11 起)(C++11 起)(在 C++20 中移除) |
比较 unordered_set 中的值 (函数模板) |
(C++11 起)(C++11 起)(在 C++20 中移除) |
比较 unordered_multiset 中的值 (函数模板) |
按字典顺序比较两个 queue 的值(函数模板) | |
按字典顺序比较两个 stack 的值(函数模板) | |
比较底层的迭代器 (函数模板) | |
(C++11 起)(C++11 起)(在 C++20 中移除)(C++11 起)(C++11 起)(C++11 起)(C++11 起)(C++20 起) |
比较底层的迭代器 (函数模板) |
(在 C++20 中移除) |
比较两个 istream_iterator (函数模板) |
(在 C++20 中移除) |
比较两个 istreambuf_iterator (函数模板) |
(在 C++20 中移除) |
比较两个复数或一个复数和一个标量 (函数模板) |
比较两个 valarray 或一个 valarray 与一个值 (函数模板) | |
(C++11 起)(C++11 起)(在 C++20 中移除) |
比较两个伪随机数引擎的内部状态 (函数) |
(C++11 起)(C++11 起)(在 C++20 中移除) |
比较两个分布对象 (函数) |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20 起) |
将 sub_match 与另一个 sub_match 、字符串或字符比较(函数模板) |
(在 C++20 中移除) |
按字典顺序比较两个匹配结果中的值 (函数模板) |
(在 C++20 中移除) |
比较两个 regex_iterator ( std::regex_iterator<BidirIt,CharT,Traits> 的公共成员函数) |
(在 C++20 中移除) |
比较两个 regex_token_iterator ( std::regex_token_iterator<BidirIt,CharT,Traits> 的公共成员函数) |
(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(在 C++20 中移除)(C++20 起) |
比较两个 thread::id 对象(函数) |
命名空间 std::rel_ops 提供了泛型运算符 !=、>、<= 和 >=
在头文件
<utility> 中定义 | |
在命名空间
std::rel_ops 中定义 | |
(在 C++20 中弃用) |
基于用户定义的 operator== 和 operator< 自动生成比较运算符 (函数模板) |
[编辑] 缺陷报告
以下行为变更缺陷报告被追溯应用于先前发布的 C++ 标准。
DR | 应用于 | 已发布行为 | 正确行为 |
---|---|---|---|
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 文档 关于 比较运算符
|