Я изо всех сил пытаюсь предоставить нового оператора космического корабля для класса enum. Возьмем следующий пример:
#include <cstdio>
#include <iostream>
#include <compare>
#include <cstdint>
enum class Animals : uint8_t
{
Bird = 27, //those values are just for making a point
Tiger = 5,
Ant = 100,
Snake = 45,
Wale = 17
};
//auto operator<=(const Animals& lhs, const Animals& rhs) = delete;
//auto operator>=(const Animals& lhs, const Animals& rhs) = delete;
//auto operator<(const Animals& lhs, const Animals& rhs) = delete;
//auto operator>(const Animals& lhs, const Animals& rhs) = delete;
auto operator<=>(const Animals& lhs, const Animals& rhs)
{
std::cout << "comparing via overloaded <=> operator\n";
//order the animals by their size in real life
// Ant < Bird < Snake < Tiger < Wale
//for this MVCE only Ant < Tiger is in here:
if (lhs == Animals::Ant && rhs == Animals::Tiger)
return -1;
return 0; //all unimplemented ones are treated as equal
}
int main(void)
{
if (Animals::Ant < Animals::Tiger)
std::puts("success (do I see the prompt of the overloaded operator?)");
else
std::puts("seems to use uint8_t comparison instead");
return 0;
}
Но, очевидно, я здесь что-то не так, поскольку мой main()
все еще говорит мне, что муравьи больше, чем тигры. Как видите, я попытался явно удалить операторы сравнения по умолчанию, чтобы заставить компилятор использовать мой пользовательский космический корабль-один, но безуспешно.
Когда я явно вызываю auto result = Animals::Ant <=> Animals::Tiger
, я получаю ambiguous overload for 'operator<=>' (operand types are 'Animals' and 'Animals')
. Но это, похоже, связано с моей подписью операторов (вместо этого я использую const
Животные).
Можно ли перезаписать оператор для моего Enum (не мешая операторам для его базового типа "uint8_t"?
Действительно, и он также должен принимать типы указателей и перечислений по значению: en.cppreference.com/w/cpp/language/… (абзац справа выше, пример для трехстороннего сравнения). Обратите внимание, что gcc и clang, похоже, не согласны с результатами, это похоже на ошибку в одном из компиляторов (не знаю, какой из них прав): godbolt.org/z/vKsPG9x1q
Оператор должен возвращать одну из категорий заказов вместо auto int, возможно std::weak_ordering
cppreference говорит, что enum нужно брать по значению. и взять по значению, решить проблему (нет необходимости возвращать специальный порядок)
Похоже, оператор космического корабля для enums работает только для clang 11+, icc-2021 и msvc-19.29. Но не на любой версии gcc. Даже после изменения типа возвращаемого значения на int и передачи по значению. godbolt.org/z/x6PjWdesT
(продолжение моего комментария), но эталонная версия приводит к неоднозначному результату godbolt.org/z/qeh6nzWME (т.е. она действительно учитывается компилятором). не уверен, что происходит (также не уверен, почему он должен принимать значение).
также неоднозначно в msvc godbolt.org/z/xYYsnjvd6
С вашим operator<=>
что-то не так:
int
. В этом случае, вероятно, std::weak_ordering
прав.То есть:
constexpr auto operator<=>(Animals lhs, Animals rhs) -> std::weak_ordering
{
//order the animals by their size in real life
// Ant < Bird < Snake < Tiger < Wale
//for this MVCE only Ant < Tiger is in here:
if (lhs == Animals::Ant && rhs == Animals::Tiger)
return std::weak_ordering::less;
return std::weak_ordering::equivalent;
}
Тем не менее, существуют расхождения в реализации того, как обрабатывать кандидатов на переписывание с помощью <=>
. clang и msvc реализуют правила, которые, вероятно, должны быть, а именно то, что наш объявленный пользователем operator<=>
подавляет все встроенные реляционные и трехсторонние операторы сопоставления, так что Animals::Ant < Animals::Tiger
вызывает наш operator<=>
. Но gcc реализует то, что технически буквально говорят правила, а именно то, что Animals::Ant <=> Animals::Tiger
оценивает наш оператор, а использование <
— нет. Для этого открыт отчет об ошибке gcc (#105200 ), где один из разработчиков gcc указывает на проблему с формулировкой. Это кажется мне проблемой формулировки, а не фактической проблемой дизайна, поэтому я открываю основную проблему об этом ( # 205).
Чтобы это работало на gcc, вы также должны пройти и добавить их самостоятельно (примечание: всегда по значению):
constexpr auto operator<(Animals lhs, Animals rhs) -> bool {
return (lhs <=> rhs) < 0;
}
constexpr auto operator<=(Animals lhs, Animals rhs) -> bool {
return (lhs <=> rhs) <= 0;
}
constexpr auto operator>(Animals lhs, Animals rhs) -> bool {
return (lhs <=> rhs) > 0;
}
constexpr auto operator>=(Animals lhs, Animals rhs) -> bool {
return (lhs <=> rhs) >= 0;
}
спасибо, это очень помогло. Но почему мой const&
был неправильным? Согласно верхней таблице здесь сигнатура оператора как freestanding-функции должна быть: /*R*/ operator <=>(const T &a, const T2 &b);
хорошо, я думаю, что @Yksisarvinen ответил на это в комментарии под моим вопросом. Для перечислений это должно быть operator<=>(T a, T b)
Ваша операция возвращает необработанный
int
. Я считаю, что возвращаемый тип должен быть категорией сравнения, чтобы сантехника работала.