Операторы двустороннего сравнения должны быть функциями, не являющимися членами, если:
Новый оператор трехстороннего сравнения C ++ 20 имеет симметричные правила генерации. Поиск имени для выражения a@b
, где @
является оператором двустороннего сравнения, выполняется в порядке a@b
, a<=>b
а также b<=>a
(с этим порядком предпочтения в случае неоднозначности при выборе лучшего соответствия из набора разрешений перегрузки). Увидеть P0515R2 для деталей. Это означает, что оператор <=>
может быть функцией-членом и все же позволять первому операнду не относиться к этому типу класса.
Документ, однако, содержит эту заметку:
Обычно оператор<=> должна быть просто функцией-членом; ты еще будешь
получить преобразования по каждому параметру из-за симметричной генерации
правила в §2.3. В редком случае, который вы также хотите поддержать
преобразования по обоим параметрам одновременно (для включения сравнения
два объекта, ни один из которых не относится к этому типу, но с использованием этого типа
функция сравнения), сделайте его не членом.
Если я правильно понимаю, это говорит, что не-членская реализация должна быть необходима, только если требуется неявное преобразование для обоих операндов одновременно? Это верно? Могу ли я увидеть реальный пример, когда это необходимо? Я думаю об этом, хотя это не похоже на действительный пример:
struct foo
{
foo(int const x)
: data{ x } {}
foo(std::string_view x)
: data{std::stoi(x.data())}{}
friend auto operator<=>(foo const & lhv, foo const & rhv) noexcept
{
return lhv.data <=> rhv.data;
}
private:
int data;
};int main()
{
assert(foo {42} == foo {"42"}); // OK
assert(42 == std::string_view("42")); // ??
}
Вот иллюстративный (хотя и не обязательно практический) пример:
struct A {
int i;
};
struct B {
B(A a) : i(a.i) { }
int i;
};
strong_ordering operator<=>(B const& lhs, B const& rhs) {
return lhs.i <=> rhs.i;
}
A{2} == A{2}; // okay, true
A{2} < A{1}; // okay, false
Мы находим кандидата, принимающего два B
в глобальном масштабе, и это жизнеспособно, поэтому мы преобразуем оба аргумента и используем его. Если этот оператор был или функцией-членом или не членом friend
объявленный в классе, поиск имени не нашел бы его.
Обратите внимание, что в ОП, <=>
объявлен как нечлен friend
в классе. Это означает, что поиск по имени не найдет его для 42 == string_view("42")
поскольку ни один из этих аргументов не является foo
, Вам нужно добавить обычное объявление, не являющееся членом, чтобы сделать его видимым для такого поиска.
Других решений пока нет …