比較運算子
比較引數。
運算子名稱 | 語法 | 可重載 | 原型示例(對於 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] |
|
目錄 |
[編輯] 雙路比較
雙路比較運算子表示式具有以下形式
[編輯] 關係運算符
左運算元 < 右運算元 |
(1) | ||||||||
左運算元 > 右運算元 |
(2) | ||||||||
左運算元 <= 右運算元 |
(3) | ||||||||
左運算元 >= 右運算元 |
(4) | ||||||||
[編輯] 相等運算子
左運算元 == 右運算元 |
(5) | ||||||||
左運算元 != 右運算元 |
(6) | ||||||||
[編輯] 內建雙路比較運算子
對於內建雙路比較運算子,左值到右值轉換、陣列到指標轉換(C++26 前)和函式到指標轉換會應用於左運算元和右運算元。
如果應用這些轉換前 左運算元 和 右運算元 都具有陣列型別,那麼比較是被棄用的。 |
(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 值 |
如果轉換後的 左運算元 和 右運算元 中至少有一個是指標,則對兩個轉換後的運算元執行指標轉換、函式指標轉換(C++17 起)和限定轉換,以將它們轉換為複合指標型別。這兩個複合指標型別的指標按如下方式比較:
- 如果一個指標表示一個完整物件的地址,而另一個指標
- 表示另一個不同的非陣列完整物件的尾後地址,或
- 表示另一個不同的完整陣列物件的最後一個元素之後一個位置的地址,
- 則比較的結果是未指明的。
- 否則,如果兩個指標都是空指標,都指向同一個函式,或都表示相同的地址(即,它們指向同一個物件,或都是同一個物件的尾後指標),那麼它們比較為相等。
- 否則,指標比較為不相等。
如果轉換後的 左運算元 和 右運算元 中至少有一個是成員指標,則對兩個轉換後的運算元執行成員指標轉換、函式指標轉換(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 值 |
如果轉換後的 左運算元 和 右運算元 都是指標,則對兩個轉換後的運算元執行指標轉換、函式指標轉換(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 前)非靜態資料成員在記憶體中的位置按其宣告順序排列。
儘管比較隨機來源(例如,並非都指向同一陣列的成員)的指標的結果是未指定的,但許多實現為指標提供了嚴格全序,例如,如果它們被實現為連續虛擬地址空間內的地址。那些不提供嚴格全序的實現(例如,指標的某些位不是記憶體地址的一部分而必須在比較時被忽略,或者需要額外的計算,或者指標和整數之間不是一一對應的關係),會為指標提供 std::less 的特化,以保證這種全序關係。這使得可以將所有隨機來源的指標作為鍵用在標準關聯容器中,如 std::set 或 std::map。
對於同時滿足 EqualityComparable 和 LessThanComparable 的型別,C++ 標準庫區分了相等性 (equality) 和等價性 (equivalence)。相等性是表示式 a == b 的值,而等價性是表示式 !(a < b) && !(b < a) 的值。
指標與空指標常量之間的比較已透過 N3624 中包含的 CWG 問題 583 的解決方案移除。
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
特性測試宏 | 值 | 標準 | 特性 |
---|---|---|---|
__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) |
比較兩個 durations (函式模板) |
(C++11)(C++11)(C++20 中移除)(C++11)(C++11)(C++11)(C++11)(C++20) |
比較兩個時間點 (函式模板) |
(在 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++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
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 文件中關於比較運算子的內容
|