Default comparisons (since C++20)
Функции операторов сравнения могут быть явно определены по умолчанию, чтобы запросить у компилятора генерацию соответствующего сравнения по умолчанию для класса.
Содержание |
Определение
Функция оператора сравнения по умолчанию
— это нетемплейтная функция оператора сравнения (т.е.
<=>
,
==
,
!=
,
<
,
>
,
<=
, или
>=
), удовлетворяющая всем следующим условиям:
-
Это
нестатический член-функция
или
дружественная функция
некоторого класса
C. -
Она
определена как defaulted
в
Cили в контексте, гдеCявляется полным типом . -
Она имеет два параметра типа
const
C
&
или два параметра типа
C, где неявный объектный параметр (если присутствует) считается первым параметром.
Такая функция оператора сравнения называется
стандартной функцией оператора сравнения для класса
C
.
struct X { bool operator==(const X&) const = default; // OK bool operator==(const X&) = default; // Ошибка: тип неявного параметра объекта // равен X& bool operator==(this X, X) = default; // OK }; struct Y { friend bool operator==(Y, Y) = default; // OK friend bool operator==(Y, const Y&) = default; // Ошибка: разные типы параметров }; bool operator==(const Y&, const Y&) = default; // Ошибка: не является дружественной функцией для Y
Поиск имен и проверки доступа при неявном определении функции оператора сравнения выполняются из контекста, эквивалентного ее телу функции. Определение функции оператора сравнения как заданной по умолчанию, которое появляется в классе, должно быть первым объявлением этой функции.
Порядок сравнения по умолчанию
Для класса
C
список подобъектов формируется из следующих подобъектов в указанном порядке:
-
Прямые базовые классы объекта
Cв порядке объявления. -
Нестатические
члены данных
класса
Cв порядке объявления.
-
- Если любой член-подобъект имеет тип массива, он раскрывается в последовательность своих элементов в порядке возрастания индекса. Раскрытие рекурсивно: элементы массива, имеющие тип массива, будут раскрываться снова, пока не останется подобъектов типа массива.
Для любого объекта
x
типа
C
, в следующих описаниях:
- Пусть n будет количеством подобъектов в (расширенном) списке подобъектов для x .
- Пусть x_i будет i -м подобъектом в (расширенном) списке подобъектов для x , где x_i формируется последовательностью преобразований производный-в-базовый , выражений доступа к членам класса и выражений индексации массива , примененных к x .
struct S {}; struct T : S { int arr[2][2]; } t; // Список подобъектов для "t" состоит из следующих 5 подобъектов в порядке: // (S)t → t[0][0] → t[0][1] → t[1][0] → t[1][1]
Трехстороннее сравнение
Оператор operator <=> для типа класса может быть определен как стандартный с любым возвращаемым типом.
Типы категорий сравнения
Существует три типа категорий сравнения:
| Тип | Эквивалентные значения.. | Несравнимые значения.. |
|---|---|---|
| std::strong_ordering | неразличимы | не допускаются |
| std::weak_ordering | различимы | не допускаются |
| std::partial_ordering | различимы | допускаются |
Синтезированное трёхстороннее сравнение
Синтезированное трёхстороннее сравнение
типа
T
между glvalue
a
и
b
того же типа определяется следующим образом:
-
Если разрешение перегрузки для
a
<=>
b
приводит к применимому кандидату и может быть явно преобразовано в
Tс использованиемstatic_cast, синтезированное сравнение имеет вид 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
Заполнитель типа возвращаемого значения
Если объявленный возвращаемый тип оператора трехстороннего сравнения по умолчанию (
operator
<=>
) для типа класса
C
равен
auto
, то возвращаемый тип выводится из возвращаемых типов трехсторонних сравнений между соответствующими подобъектами объекта
x
типа
C
.
Для каждого подобъекта x_i в (расширенном) списке подобъектов для x :
- Выполнить разрешение перегрузки для 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 выполняется путём сравнения соответствующих подобъектов x_i и y_i в порядке возрастания 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 // функции операторов двустороннего сравнения не требуют явного определения: // operator== неявно объявляется (см. ниже) // разрешение перегрузки других кандидатов выберет переписанные кандидаты 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
и объекта
x
типа
C
, если в (расширенном) списке подобъектов для
x
существует подобъект
x_i
такой, что разрешение перегрузки для
x_i
==
x_i
не приводит к пригодному кандидату, то оператор
operator
==
по умолчанию определяется как удалённый.
Пусть x и y будут параметрами оператора operator == по умолчанию. Обозначим каждый подобъект в (расширенном) списке подобъектов для x и y как x_i и y_i соответственно. Сравнение по умолчанию между x и y выполняется путём сравнения соответствующих подобъектов x_i и y_i в порядке возрастания 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
<=>
, определенного как defaulted, неявно объявляется функция оператора
==
. Каждый неявно объявленный
operator
==
имеет те же права доступа и
определение функции
, а также находится в той же
области видимости класса
, что и соответствующий оператор
operator
<=>
, со следующими изменениями:
- Идентификатор декларатора заменяется на operator == .
- Возвращаемый тип заменяется на bool .
template<typename T> struct X { friend constexpr std::partial_ordering operator<=>(X, X) requires (sizeof(T) != 1) = default; // неявно объявляет: friend constexpr bool operator==(X, X) // requires (sizeof(T) != 1) = default; [[nodiscard]] virtual std::strong_ordering operator<=>(const X&) const = default; // неявно объявляет: [[nodiscard]] virtual bool // operator==(const X&) const = default; };
Вторичное сравнение
Вторичная функция оператора сравнения (
!=
,
<
,
>
,
<=
, или
>=
) для типа класса может быть определена как defaulted с возвращаемым типом
bool
.
Пусть
@
будет одним из пяти вторичных операторов сравнения. Для каждого оператора по умолчанию
operator@
с параметрами
x
и
y
выполняется до двух разрешений перегрузки (не рассматривая оператор по умолчанию
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, функция является стандартной };
Ключевые слова
Отчеты о дефектах
Следующие отчеты об изменениях в поведении, являющиеся дефектами, были применены ретроактивно к ранее опубликованным стандартам C++.
| DR | Applied to | Behavior as published | Correct behavior |
|---|---|---|---|
| CWG 2539 | C++20 |
синтезированное трёхстороннее сравнение выбирало бы
static_cast даже если явное преобразование недоступно |
не выбирает
static_cast в этом случае |
| CWG 2546 | C++20 |
стандартизированный вторичный
operator@
не был
определён как удалённый, если разрешение перегрузки x @ y выбирает непригодного переписанного кандидата |
определяется как удалённый
в этом случае |
| CWG 2547 | C++20 |
было неясно, могут ли функции операторов сравнения
для не-классов быть стандартизированными |
они не могут быть стандартизированы |
| CWG 2568 | C++20 |
неявное определение функций операторов сравнения
может нарушать правила доступа к членам |
проверки доступа выполняются
из контекста, эквивалентного их телам функций |
Смотрите также
- разрешение перегрузки при вызове перегруженного оператора
- Встроенный оператор трехстороннего сравнения
- Перегрузка операторов для операторов сравнения