Подтвердить что ты не робот

Каким образом оператор трехстороннего сравнения отличается от вычитания?

В С++ 20 появился новый оператор сравнения <=>. Однако я думаю, что в большинстве случаев простое вычитание хорошо работает:

int my_strcmp(const char *a, const char *b) {
    while (*a == *b && *a != 0 && *b != 0) {
        a++, b++;
    }
    // Version 1
    return *a - *b;
    // Version 2
    return *a <=> *b;
    // Version 3
    return ((*a > *b) - (*a < *b));
}

Они имеют тот же эффект. Я не могу понять разницу.

4b9b3361

Ответ 1

Оператор решает проблему с числовым переполнением, которое вы получаете с вычитанием: если вы вычитаете большое положительное число из отрицательного значения, близкого к INT_MIN, вы получаете число, которое не может быть представлено как int, таким образом вызывая поведение undefined.

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

Это только одна проблема, которую требует новый оператор. Раздел 2.2.3 Herb Sutter Согласованный сравнительный документ рассказывает об использовании <=> с другими типами данных языка, где вычитание может приводить к несогласованным результатам.

Ответ 2

Вот некоторые случаи, в которых вычитание не будет работать:

  • unsigned.
  • Операнды, вызывающие переполнение целых чисел.
  • Определяемые пользователем типы, которые не определяют operator - (возможно, потому что это не имеет смысла - можно определить порядок без определения понятия расстояния).

Я подозреваю, что этот список не является исчерпывающим.

Конечно, можно придумать обходные пути, по крайней мере, для # 1 и # 2. Но целью operator <=> является инкапсуляция этого уродства.

Ответ 3

Здесь есть несколько значимых ответов, но Херб Саттер в в своей статье говорит:

< = > для разработчиков типов: код пользователя (включая общий код) за пределами реализации оператора <= > должен почти никогда не ссылаться непосредственно на <= > (как уже было обнаружено как хорошая практика на других языках);

Таким образом, даже если не было никакой разницы, точка оператора различна: чтобы авторы классов могли создавать операторы сравнения.

Основное различие между оператором вычитания и оператором "космического корабля" (согласно предложению Саттера) заключается в том, что перегрузка operator- дает вам оператор вычитания, тогда как перегрузка operator<=>:

  • дает вам 6 основных операторов сравнения (даже если вы объявляете оператора как default: никакого кода для записи!);
  • объявляет, сопоставим ли ваш класс, сортируется и является ли порядок полным или частичным (сильный/слабый в предложении Sutter);
  • позволяет проводить гетерогенные сравнения: вы можете перегрузить его, чтобы сравнить свой класс с любым другим типом.

Другие значения относятся к возвращаемому значению: operator<=> возвращает enum класса, класс указывает, является ли тип сортируемым и является ли тип сильным или слабым. Возвращаемое значение преобразуется в -1, 0 или 1 (хотя Саттер оставляет место для типа возврата, а также указывает расстояние, как это делает strcmp). В любом случае, если принять возвращаемое значение -1, 0, 1, мы наконец получим истинную функцию signum в С++! (signum(x) == x<=>0)