Оператор рівності не визначається для реалізації спеціального оператора космічного корабля в C ++ 20


51

Я стикаюся з дивною поведінкою з новим оператором космічного корабля <=>в C ++ 20. Я використовую компілятор Visual Studio 2019 /std:c++latest.

Цей код складається добре, як і очікувалося:

#include <compare>

struct X
{
    int Dummy = 0;
    auto operator<=>(const X&) const = default; // Default implementation
};

int main()
{
    X a, b;

    a == b; // OK!

    return 0;
}

Однак якщо я зміню X на це:

struct X
{
    int Dummy = 0;
    auto operator<=>(const X& other) const
    {
        return Dummy <=> other.Dummy;
    }
};

Я отримую таку помилку компілятора:

error C2676: binary '==': 'X' does not define this operator or a conversion to a type acceptable to the predefined operator

Я спробував це і на кланге, і я отримую подібну поведінку.

Я би вдячний деяким поясненням того, чому реалізація за замовчуванням генерується operator==правильно, але спеціальна - ні.

Відповіді:


50

Це за дизайном.

[class.compare.default] (моє наголос)

3 Якщо визначення класу не явно декларує функцію == оператора, але оголошує функцію оператора тристороннього порівняння за замовчуванням, функція ==оператора оголошується неявно з тим же доступом, що і функція оператора тристороннього порівняння. Неявно оголошений ==оператор для класу X є вбудованим членом і визначається як дефолт у визначенні X.

Лише дефолт <=>дозволяє синтезованому ==існувати. Обґрунтуванням є те, що такі класи як std::vectorне можуть використовувати значення за замовчуванням <=>. Крім того, використання <=>для ==не є найбільш ефективним способом порівняння векторів. <=>повинні дати точне впорядкування, тоді як ==може достроково відмовитись , порівнявши спочатку розміри.

Якщо клас робить щось особливе у своєму тристоронньому порівнянні, він, ймовірно, повинен зробити щось особливе у своєму ==. Таким чином, замість генерування нечутливого за замовчуванням мова залишає це програмісту.


4
Це, безумовно, розумно, якщо космічний корабель не баггі. Потенційно вкрай неефективно , хоча ...
Deduplicator

1
@ Дедуплікатор - чутливість суб’єктивна. Деякі кажуть, що неефективна реалізація мовчазної генерації не є розумною.
StoryTeller - Невідповідна Моніка

45

Під час стандартизації цієї функції було вирішено, що рівність та впорядкованість повинні логічно розділятися. Таким чином, використання тестування рівності ( ==і !=) ніколи не посилатиметься operator<=>. Однак це все ще вважалося корисним мати можливість замовчувати їх обох однією декларацією. Тож якщо за замовчуванням operator<=>було вирішено, що ви також маєте на увазі за замовчуванням operator==(якщо ви не визначите його пізніше або не визначили його раніше).

Щодо того, чому було прийнято таке рішення , то основні міркування виглядають так. Розглянемо std::string. Упорядкування двох рядків є лексикографічним; кожен символ має своє ціле значення порівняно з кожним символом в іншому рядку. Перша нерівність призводить до впорядкування.

Однак тестування рівності струн має коротке замикання. Якщо два рядки не мають однакової довжини, то взагалі немає сенсу робити порівняння з символами; вони не рівні. Тож якщо хтось робить тестування рівності, ви не хочете робити це у довгостроковій формі, якщо можете коротко замикатись.

Виявляється, багато типів, які потребують замовлення, визначеного користувачем, також пропонують якийсь механізм короткого замикання для тестування рівності. Щоб люди operator<=>не могли реалізовувати лише та відкидати потенційну ефективність, ми ефективно змушуємо всіх робити те і інше.


5
Це набагато краще пояснення, ніж прийнята відповідь
доповідь

17

Інші відповіді дуже добре пояснюють, чому мова така. Я просто хотів додати, що у випадку, коли це не очевидно, можна, звичайно, забезпечити користувачем, який operator<=>має замовчування operator==. Вам просто потрібно чітко написати дефолт operator==:

struct X
{
    int Dummy = 0;
    auto operator<=>(const X& other) const
    {
        return Dummy <=> other.Dummy;
    }
    bool operator==(const X& other) const = default;
};
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.