Чем трехсторонний оператор сравнения отличается от вычитания?

Там новый оператор сравнения <=> в С ++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));
}

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

3 ответа

Решение

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

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

Это только одна проблема, решаемая новым оператором. В разделе 2.2.3 " Сравнительного документа Херба Саттера" говорится об использовании <=> с другими типами данных языка, где вычитание может привести к противоречивым результатам.

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

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

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

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

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

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

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

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

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

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

Другие вопросы по тегам