автоматическая генерация некоторых операторов, имея в наличии противоположные

Если в некотором классе определены операторы < ==, что мешает компилятору автоматически генерировать операторы <=, >, >=, !=? Насколько я помню, где то в стандартной библиотеке даже определены такие шаблонные операторы, а в стандартных итераторах определены все эти операторы как:

если b < a, то a > b, если !(b < a) то a >= b и так далее

Зачем это писать, если можно пользоваться общим шаблоном? И почему современные компиляторы не могут просто понять это, зная что такое меньше и зная что такое равно?...


Ответы (2 шт):

Автор решения: Croessmah

Если в некотором классе определены операторы < ==, что мешает компилятору автоматически генерировать операторы <=, >, >=, !=?

Например, то, что логика класса может быть отлична от "человеческой логики". Как компилятор будет добавлять логику в ваш класс, если вы явно указали, что класс поддерживает только < и ==? Изначально, такое поведение не предусмотрено языком, соответственно, нужно либо менять часть стандарта, отвечающее за это, что, возможно, поломает чей-то код, либо вводить какой-то дополнительный синтаксис, с помощью которого можно указать, что ваш класс действительно хочет, чтобы компилятор сделал эту работу для ваших операторов.

C++20 добавляет в язык новый оператор <=>, который предоставляет возможность реализовать т.н. "three-way сравнение". Данное сравнение давно представлено в C-функциях на подобии strcmp, которая возвращает значение меньше, равное или большее нуля в зависимости от лексикографического порядка значений аргументов. Для работы данного оператора введена новая механика, работающая для операторов отношения (<, >, <=, >=), приблизительно, следующим образом:

  • если пользователь определил свой <=>, и не определил какие-то операторы сравнения, то при использовании этих операторов, оператор <=> включается в список подходящих функций, причем, включая и обратный порядок операндов.
  • если пользователь определил свой <=>, и определил какие-то операторы сравнения, то при использовании этих операторов будут использованы именно предоставленные пользователем перегрузки.

Если в результате разрешения перегрузки был выбран перегруженный оператор <=>, то выражение x @ y, где @ - оператор сравнения, производится по правилу (x <=> y) @ 0, либо по правилу, 0 @ (y <=> x) если порядок аргументов нужно инвертировать.

Разберем на примере перегрузку оператора <=>.

#include <iostream>
#include <compare>

struct Some
{    
};

bool operator>(Some const &, int)
{
    std::cout << "operator>(Some const &, int)" << std::endl;
    return false;
}

std::strong_ordering operator<=>(Some const &, Some const &)
{
    std::cout << "operator<=>(Some const &, Some const &)" << std::endl;
    return std::strong_ordering::less;
}

std::strong_ordering operator<=>(Some const &, int)
{
    std::cout << "operator<=>(Some const &, int)" << std::endl;
    return std::strong_ordering::less;
}

int main() 
{
    Some s1, s2;
    s1 < s2; //operator<=>(Some const &, Some const &)
    s1 > 2;  //operator>(Some const &, int)
    s1 < 2;  //operator<=>(Some const &, int)
    2 < s1;  //operator<=>(Some const &, int)
    2 > s1;  //operator<=>(Some const &, int)
}

Для типа Some предоставлено три оператора сравнения.

s1 < s2 - будет сравниваться с помощью operator<=>(Some const &, Some const &) по правилу (s1 <=> s2) < 0. Результат, естевственно, true, т.к. наши операторы <=> не производят сравнения, а просто всегда считают, что первый аргумент меньше второго (возвращают std::strong_ordering::less).

В выражении s1 > 2 будет использован перегруженный operator>(Some const &, int).

В выражении s1 < 2 будет выбран operator<=>(Some const &, int) и сравнение будет происходить по правилу (s1 <=> 2) < 0.

В выражении 2 < s1 также будет выбран operator<=>(Some const &, int), но для его выбора необходимо инвертировать порядок аргументов и сравнение уже будет происходить по правилу 0 < (s1 <=> 2).

В выражении 2 > s1 также будет выбран operator<=>(Some const &, int) и сравнение будет происходить по правилу 0 > (s1 <=> 2).Заметьте, перегруженный пользователем operator>(Some const &, int) выбран не будет, т.к. для "старых" (two-way comparation) операторов инвертирование порядка аргументов не предусмотрено.

Таким образом, начиная с C++20, в классе достаточно будет перегрузить один оператор <=>, и, используя его, компилятор будет строить код для других сравнений, перегрузки операторов которых пользователь не предоставил.

Также необходимо будет предоставить оператор == для нужного типа аргументов, тогда компилятор будет генерировать на основе == оператор !=.

Подробнее об этом можете почитать на странице https://en.cppreference.com/w/cpp/language/operator_comparison в секции "Three-way comparison".

→ Ссылка