Надеюсь, многие знают, что в C++20 появилась замечательная штука operator<=>. Стоит его определить, как компилятор на его основе выводит другие операторы сравнения, вроде operator< или operator>=.
Причем, что особенно хорошо, можно попросить компилятор сгенерировать operator<=> для нас автоматически:
struct example {
... // Какие-то поля.
auto operator<=>(const example &) const = default;
};
|
И все. Остальное за нас сделает компилятор.
Однако, думаю, не все знают, что если нам не подходит штатная реализация и мы пишем operator<=> сами, то компилятор потребует, чтобы мы еще и определили свой собственный operator==.
Например, допустим, что у нас в example три поля, а в сравнении должно участвовать только два из них:
struct example {
int m_a;
int m_b;
int m_c;
auto operator<=>(const example & o) const noexcept
{
return std::tie(m_a, m_c) <=> std::tie(o.m_a, o.m_c);
}
};
|
В этом случае мы внезапно© обнаружим, что у нас нет operator==.
Таков путь: если мы определили свой operator<=>, то компилятор не будет автоматически выводить равенство и неравенство, а потребует, чтобы мы предоставили operator==.
Может быть вам будет интересно почему?
Если да, то лучше всего прочитать раздел с мотивацией из предложения P1185 и вот этот ответ на Stackoverflow. В двух же словах: реализация сравнения на строгое равенство на базе operator<=> может быть весьма неэффективна.
Например, представим, что мы сравниваем две строки: s1="AA" и s2="AAA".
При лексикографическом сравнении s1 меньше, чем s2: общая подстрока в s1 и s2 совпадает, но в s1 символов меньше.
Когда мы для наших строк s1 и s2 вызываем operator<=>, то этот оператор пытается выяснить как именно соотносятся s1 и s2: меньше ли s1, чем s2, или же s1 больше, чем s2, или же они равны.
Теперь допустим, что в s1 находится миллион символов "A", а в s2 -- миллион и один символ "A". При вызове operator<=> придется сравнить миллион символов в общей подстроке прежде чем мы сможем понять, что s1 все-таки меньше, т.к. у нее символов меньше.
При этом мы не можем прервать сравнение раньше. Допустим, что в s1 будет девятьсот девяносто девять тысяч и девятьсот девяносто девять символов "A", а за ними единственный символ "B", тогда как в s2 все миллион + один символ -- это "A". В таком случае строка s1 окажется больше, не смотря на то, что символов в ней меньше.
Однако, если нам не нужно выяснять общее соотношение между s1 и s2, а достачно просто понять равны ли они или нет, то мы можем начать со сравения длин и лишь затем переходить к посимвольному сравнению.
Когда компилятор по нашей просьбе сам генерирует operator<=>, то он эти фокусы учитывает (под катом небольшой тестовый бенчмарк, который позволяет убедится, что сравнение двух неравных векторов требует минимального времени). Однако, если operator<=> предоставляет пользователь, то у компилятора нет уверенности в том, что пользователь написал свой operator<=> с учетом возможностей оптимизации операции "строгое равенство". И просит нас взять ответственность за operator== на себя.