Namespaces
Variants

Default comparisons (since C++20)

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

Функции операторов сравнения могут быть явно определены по умолчанию, чтобы запросить у компилятора генерацию соответствующего сравнения по умолчанию для класса.

Содержание

Определение

Функция оператора сравнения по умолчанию — это нетемплейтная функция оператора сравнения (т.е. <=> , == , != , < , > , <= , или >= ), удовлетворяющая всем следующим условиям:

Такая функция оператора сравнения называется стандартной функцией оператора сравнения для класса 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 , в следующих описаниях:

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 :

  1. Выполнить разрешение перегрузки для x_i <=> x_i ; если разрешение перегрузки не приводит к пригодному кандидату, стандартный operator <=> определяется как удалённый.
  2. Обозначить версию типа 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, функция является стандартной
};

Ключевые слова

default

Отчеты о дефектах

Следующие отчеты об изменениях в поведении, являющиеся дефектами, были применены ретроактивно к ранее опубликованным стандартам 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 неявное определение функций операторов сравнения
может нарушать правила доступа к членам
проверки доступа выполняются
из контекста, эквивалентного
их телам функций

Смотрите также