Namespaces
Variants

Comparison operators

From cppreference.net
C++ language
General topics
Flow control
Conditional execution statements
Iteration statements (loops)
Jump statements
Functions
Function declaration
Lambda function expression
inline specifier
Dynamic exception specifications ( until C++17* )
noexcept specifier (C++11)
Exceptions
Namespaces
Types
Specifiers
constexpr (C++11)
consteval (C++20)
constinit (C++20)
Storage duration specifiers
Initialization
Expressions
Alternative representations
Literals
Boolean - Integer - Floating-point
Character - String - nullptr (C++11)
User-defined (C++11)
Utilities
Attributes (C++11)
Types
typedef declaration
Type alias declaration (C++11)
Casts
Memory allocation
Classes
Class-specific function properties
Special member functions
Templates
Miscellaneous

比较参数。

运算符名称 语法 可重载 原型示例(针对 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]
注释
  • 当内置运算符返回 bool 时,大多数 用户定义重载 也返回 bool ,以便用户定义运算符可以像内置运算符一样使用。但在用户定义运算符重载中,可以使用任何类型作为返回类型(包括 void )。
  • U 可以是包括 T 在内的任何类型。
  1. 1.0 1.1 R operator<=> 的返回类型( 详见下文

目录

双向比较

双向比较运算符表达式的形式为

关系运算符
lhs < rhs (1)
lhs > rhs (2)
lhs <= rhs (3)
lhs >= rhs (4)
说明:由于原文中所有文本内容均为C++运算符和参数名(lhs/rhs),根据要求保留C++术语不翻译,仅需保持表格结构不变。所有HTML标签、属性及 标签内内容均已完整保留。
相等运算符
lhs == rhs (5)
lhs != rhs (6)
说明: - 保留了所有HTML标签和属性 - ` `标签内的运算符`==`和`!=`未翻译 - C++术语"lhs"和"rhs"(left-hand side/右手边,right-hand side/左手边)按专业惯例保留不译 - 仅对表格结构进行了呈现,未添加额外翻译内容
1) lhs 小于 rhs 时返回 true ,否则返回 false
2) lhs 大于 rhs 则返回 true ,否则返回 false
3) lhs 小于或等于 rhs 时返回 true ,否则返回 false
4) lhs 大于或等于 rhs 时返回 true ,否则返回 false
5) lhs 等于 rhs 时返回 true ,否则返回 false
6) lhs 不等于 rhs 时返回 true ,否则返回 false

内置双向比较运算符

对于内置的双向比较运算符, 左值到右值转换 数组到指针转换 (直至 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 前) ),或递归地指向这些成员的子对象,则后声明的成员的指针比较结果大于另一指针。
  • 否则,结果未指定。

指针全序

在每个程序中存在一个 针对指针的实现定义严格全序 。该严格全序与前述偏序保持一致:未指定的结果变为实现定义,而其他结果保持不变。

指针与严格全序的比较适用于以下情况:

(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

三路比较

三路比较运算符表达式的形式为

a <=> b

该表达式返回一个对象,使得

  • a < b 时, ( a <=> b ) < 0
  • a > b 时, ( a <=> b ) > 0
  • a b 相等/等值时, ( a <=> b ) == 0

如果其中一个操作数是 bool 类型而另一个不是,则程序非良构。

如果两个操作数都具有算术类型,或者一个操作数具有无作用域枚举类型而另一个具有整型,则对操作数应用常用算术转换,然后

  • 如果需要窄化转换(除了从整型到浮点类型),则程序非良构。
  • 否则,如果操作数具有整型,则该运算符生成类型为 std::strong_ordering 的纯右值:
  • 如果两个操作数在算术上相等,则为 std::strong_ordering::equal
  • 如果第一个操作数在算术上小于第二个,则为 std::strong_ordering::less
  • 否则为 std::strong_ordering::greater
  • 否则,操作数具有浮点类型,且该运算符生成类型为 std::partial_ordering 的纯右值。表达式 a <=> b 生成
  • 如果 a 小于 b ,则为 std::partial_ordering::less
  • 如果 a 大于 b ,则为 std::partial_ordering::greater
  • 如果 a 等价于 b - 0 <=> + 0 是等价的),则为 std::partial_ordering::equivalent
  • 如果无序( NaN <=> anything 是无序的),则为 std::partial_ordering::unordered

如果两个操作数具有相同的枚举类型 E ,则该运算符将操作数转换为 E 的底层类型,并对转换后的操作数应用 <=> 并返回结果。

如果至少有一个操作数是指向对象的指针或指向成员的指针,则对两个操作数应用 数组到指针转换 指针转换 限定转换 ,以使它们达到其 复合指针类型

对于转换后的指针操作数 p q p <=> q 返回类型为 std::strong_ordering 的纯右值:

  • 如果它们 比较相等 ,则为 std::strong_ordering::equal
  • 如果 q 比较大于 p ,则为 std::strong_ordering::less
  • 如果 p 比较大于 q ,则为 std::strong_ordering::greater
  • 如果双向比较结果未指定,则结果未指定。

否则,程序非良构。

重载

针对用户定义运算符的重载决议 中,对于指针或枚举类型 T ,以下函数签名参与重载决议:

R operator <=> ( T, T ) ;

其中 R 是上面定义的排序类别类型。

#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> 的公开成员函数)
与另一个 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
基于用户定义的 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 + = b
a - = b
a * = b
a / = b
a % = b
a & = b
a | = b
a ^ = b
a <<= b
a >>= b

++ a
-- a
a ++
a --

+ a
- a
a + b
a - b
a * b
a / b
a % b
~a
a & b
a | b
a ^ b
a << b
a >> b

! a
a && b
a || b

a == b
a ! = b
a < b
a > b
a <= b
a >= b
a <=> b

a [ ... ]
* a
& a
a - > b
a. b
a - > * b
a. * b

函数调用

a ( ... )
逗号

a, b
条件

a ? b : c
特殊运算符

static_cast 将一种类型转换为另一种相关类型
dynamic_cast 在继承层次结构内进行转换
const_cast 添加或移除 cv 限定符
reinterpret_cast 将类型转换为不相关类型
C风格转换 通过混合使用 static_cast const_cast reinterpret_cast 将一种类型转换为另一种类型
new 创建具有动态存储期的对象
delete 销毁先前由 new 表达式创建的对象并释放获取的内存区域
sizeof 查询类型的大小
sizeof... 查询 参数包 的大小 (C++11 起)
typeid 查询类型的类型信息
noexcept 检查表达式是否能抛出异常 (C++11 起)
alignof 查询类型的对齐要求 (C++11 起)

C 文档 关于 比较运算符