預設比較 (C++20 起)
比較運算子函式可以被顯式地預設化,以請求編譯器為類生成相應的預設比較。
目錄 |
[編輯] 定義
一個預設化的比較運算子函式是一個非模板比較運算子函式(即 <=>
, ==
, !=
, <
, >
, <=
, 或 >=
),滿足以下所有條件:
- 它是某個類
C
的非靜態成員或友元。 - 它在
C
中或在C
完整的上下文中被定義為預設化。 - 它有兩個型別為 const C& 的引數,或兩個型別為
C
的引數,其中隱式物件引數(如果有)被視為第一個引數。
這樣的比較運算子函式被稱為類 C
的預設化比較運算子函式。
struct X { bool operator==(const X&) const = default; // OK bool operator==(const X&) = default; // Error: the implicit object // parameter type is X& bool operator==(this X, X) = default; // OK }; struct Y { friend bool operator==(Y, Y) = default; // OK friend bool operator==(Y, const Y&) = default; // Error: different parameter types }; bool operator==(const Y&, const Y&) = default; // Error: not a friend of Y
在比較運算子函式的隱式定義中,名稱查詢和訪問檢查從與其函式體等效的上下文執行。在類中出現的預設化比較運算子函式的定義必須是該函式的第一次宣告。
[編輯] 預設比較順序
給定一個類 C
,子物件列表按以下順序形成:
C
的直接基類子物件,按宣告順序。C
的非靜態資料成員,按宣告順序。
- 如果任何成員子物件是陣列型別,它將按遞增下標的順序展開為其元素的序列。展開是遞迴的:陣列型別的陣列元素將再次展開,直到沒有陣列型別的子物件為止。
對於型別為 C
的任何物件 x,在以下描述中:
- 令 n 為 x 的(展開的)子物件列表中的子物件數量。
- 令 x_i 為 x 的(展開的)子物件列表中的第 i 個子物件,其中 x_i 是透過對 x 應用一系列派生到基的轉換、類成員訪問表示式和陣列下標表達式形成的。
struct S {}; struct T : S { int arr[2][2]; } t; // The subobject list for “t” consists of the following 5 subobjects in order: // (S)t → t[0][0] → t[0][1] → t[1][0] → t[1][1]
[編輯] 三路比較
類型別的 operator<=> 可以被定義為具有任何返回型別的預設化。
[編輯] 比較類別型別
有三種比較類別型別:
型別 | 等效值是... | 不可比較值是... |
---|---|---|
std::strong_ordering | 不可區分的 | 不允許 |
std::weak_ordering | 可區分的 | 不允許 |
std::partial_ordering | 可區分的 | 允許 |
[編輯] 合成三路比較
相同型別的 glvalue a 和 b 之間的型別 T
的合成三路比較定義如下:
- 如果 a <=> b 的過載決議產生了一個可用候選,並且可以使用
static_cast
將其顯式轉換為T
,則合成比較為 static_cast<T>(a <=> b)。 - 否則,如果滿足以下任何條件,則合成比較未定義:
- a <=> b 的過載決議找到了至少一個可行候選。
-
T
不是比較類別型別。 - a == b 的過載決議未產生可用候選。
- a < b 的過載決議未產生可用候選。
- 否則,如果
T
是 std::strong_ordering,則合成比較為
a == b ? std::strong_ordering::equal : a < b ? std::strong_ordering::less : std::strong_ordering::greater
- 否則,如果
T
是 std::weak_ordering,則合成比較為
a == b ? std::weak_ordering::equivalent : a < b ? std::weak_ordering::less : std::weak_ordering::greater
- 否則(
T
是 std::partial_ordering),合成比較為
a == b ? std::partial_ordering::equivalent : a < b ? std::partial_ordering::less : b < a ? std::partial_ordering::greater : std::partial_ordering::unordered
[編輯] 佔位符返回型別
如果類型別 C
的預設化三路比較運算子函式(operator<=>)的宣告返回型別是 auto,則返回型別從型別 C
的物件 x 的相應子物件之間的三路比較的返回型別推導。
對於 x 的(展開的)子物件列表中的每個子物件 x_i:
- 對 x_i <=> x_i 執行過載決議,如果過載決議未產生可用候選,則預設化的 operator<=> 被定義為已刪除。
- 將 x_i <=> x_i 的型別去掉 cv 限定後的版本記為
R_i
,如果R_i
不是比較類別型別,則預設化的 operator<=> 被定義為已刪除。
如果預設化的 operator<=> 未被定義為已刪除,則其返回型別被推導為 std::common_comparison_category_t<R_1, R_2, ..., R_n>。
[編輯] 非佔位符返回型別
如果預設化的 operator<=> 的宣告返回型別不是 auto,則它不能包含任何佔位符型別(例如 decltype(auto))。
如果在 x 的(展開的)子物件列表中存在子物件 x_i,使得 x_i 和 x_i 之間的宣告返回型別的合成三路比較未定義,則預設化的 operator<=> 被定義為已刪除。
[編輯] 比較結果
令 x 和 y 為預設化的 operator<=> 的引數,將 x 和 y 的(展開的)子物件列表中的每個子物件分別記為 x_i 和 y_i。 x 和 y 之間的預設三路比較透過按遞增 i 順序比較相應的子物件 x_i 和 y_i 來執行。
令 R
為(可能推匯出的)返回型別,x_i 和 y_i 之間的比較結果是型別 R
的 x_i 和 y_i 之間的合成三路比較的結果。
- 在 x 和 y 之間的預設三路比較期間,如果 x_i 和 y_i 之間的子物件級比較產生了結果 v_i,使得將 v_i != 0 上下文轉換為 bool 得到 true,則返回值為 v_i 的副本(剩餘子物件將不會被比較)。
- 否則,返回值為 static_cast<R>(std::strong_ordering::equal)。
#include <compare> #include <iostream> #include <set> struct Point { int x; int y; auto operator<=>(const Point&) const = default; /* non-comparison functions */ }; int main() { Point pt1{1, 1}, pt2{1, 2}; std::set<Point> s; // OK s.insert(pt1); // OK // two-way comparison operator functions are not required to be explicitly defined: // operator== is implicitly declared (see below) // the overload resolutions of other candidates will select rewritten candidates std::cout << std::boolalpha << (pt1 == pt2) << ' ' // false << (pt1 != pt2) << ' ' // true << (pt1 < pt2) << ' ' // true << (pt1 <= pt2) << ' ' // true << (pt1 > pt2) << ' ' // false << (pt1 >= pt2) << ' '; // false }
[編輯] 相等比較
[編輯] 顯式宣告
類型別的 operator== 可以被定義為具有返回型別 bool 的預設化。
給定一個類 C
和型別 C
的物件 x,如果在 x 的(展開的)子物件列表中存在子物件 x_i,使得 x_i == x_i 的過載決議未產生可用候選,則預設化的 operator== 被定義為已刪除。
令 x 和 y 為預設化的 operator== 的引數,將 x 和 y 的(展開的)子物件列表中的每個子物件分別記為 x_i 和 y_i。 x 和 y 之間的預設相等比較透過按遞增 i 順序比較相應的子物件 x_i 和 y_i 來執行。
x_i 和 y_i 之間的比較結果是 x_i == y_i 的結果。
- 在 x 和 y 之間的預設相等比較期間,如果 x_i 和 y_i 之間的子物件級比較產生了結果 v_i,使得將 v_i 上下文轉換為 bool 得到 false,則返回值為 false(剩餘子物件將不會被比較)。
- 否則,返回值為 true。
#include <iostream> struct Point { int x; int y; bool operator==(const Point&) const = default; /* non-comparison functions */ }; int main() { Point pt1{3, 5}, pt2{2, 5}; std::cout << std::boolalpha << (pt1 != pt2) << '\n' // true << (pt1 == pt1) << '\n'; // true struct [[maybe_unused]] { int x{}, y{}; } p, q; // if (p == q) {} // Error: operator== is not defined }
[編輯] 隱式宣告
如果類 C
沒有顯式宣告任何名為 operator== 的成員或友元,則為每個定義為預設化的 operator<=> 隱式宣告一個 運算子函式。每個隱式宣告的 operator== 具有與相應的預設化 operator<=> 相同的訪問許可權和函式定義,並在相同的類作用域中,但有以下更改:
- 宣告符識別符號被替換為 operator==。
- 返回型別被替換為 bool。
template<typename T> struct X { friend constexpr std::partial_ordering operator<=>(X, X) requires (sizeof(T) != 1) = default; // implicitly declares: friend constexpr bool operator==(X, X) // requires (sizeof(T) != 1) = default; [[nodiscard]] virtual std::strong_ordering operator<=>(const X&) const = default; // implicitly declares: [[nodiscard]] virtual bool // operator==(const X&) const = default; };
[編輯] 次要比較
類型別的次要比較運算子函式(!=
, <
, >
, <=
, 或 >=
)可以被定義為具有返回型別 bool 的預設化。
令 @
為五個次要比較運算子之一,對於每個帶有引數 x 和 y 的預設化 operator@,最多執行兩次過載決議(不將預設化的 operator@ 視為候選)以確定其是否被定義為已刪除。
- 第一次過載決議是為 x @ y 執行的。如果過載決議未產生可用候選,或者選定的候選不是重寫候選,則預設化的 operator@ 被定義為已刪除。在這些情況下,沒有第二次過載決議。
- 第二次過載決議是為 x @ y 的選定重寫候選執行的。如果過載決議未產生可用候選,則預設化的 operator@ 被定義為已刪除。
如果 x @ y 不能隱式轉換為 bool,則預設化的 operator@ 被定義為已刪除。
如果預設化的 operator@ 未被定義為已刪除,則它產生 x @ y。
struct HasNoRelational {}; struct C { friend HasNoRelational operator<=>(const C&, const C&); bool operator<(const C&) const = default; // OK, function is defaulted };
[編輯] 關鍵詞
[編輯] 缺陷報告
下列更改行為的缺陷報告追溯地應用於以前出版的 C++ 標準。
缺陷報告 | 應用於 | 釋出時的行為 | 正確的行為 |
---|---|---|---|
CWG 2539 | C++20 | 合成三路比較將選擇 static_cast 即使顯式轉換不可用 |
不選擇 在這種情況下使用 static_cast |
CWG 2546 | C++20 | 預設化的次要 operator@ 沒有 如果 x @ y 的過載決議 選擇了不可用的重寫候選,則定義為已刪除。 |
定義為已刪除 在這種情況下 |
CWG 2547 | C++20 | 尚不清楚非類型別的比較運算子 函式是否可以預設化 |
它們不能預設化 |
CWG 2568 | C++20 | 比較運算子函式的隱式定義 可能違反成員訪問規則 |
執行訪問檢查 從與它們的函式體 等效的上下文進行 |