Трехсторонний оператор сравнения против реализации, не являющейся членом - PullRequest
0 голосов
/ 23 октября 2018

Операторы двустороннего сравнения должны быть функциями, не являющимися членами, если:

  • вы хотите, чтобы первый операнд имел тип, который не является этим классом
  • вы хотите неявный типпреобразование любого из двух операндов

Новый оператор трехстороннего сравнения 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"));  // ??
}

1 Ответ

0 голосов
/ 23 октября 2018

Вот иллюстративный (хотя и не обязательно практический) пример:

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 s в глобальной области видимости, и он жизнеспособен, поэтому мы преобразуем оба аргумента и используем его.Если бы этот оператор был либо функцией-членом, либо не-членом friend, объявленным внутри класса, поиск по имени не нашел бы его.

Обратите внимание, что в OP <=> объявлено как не член friend внутри класса.Это означает, что поиск имени не найдет его для 42 == string_view("42"), поскольку ни один из этих аргументов не является foo.Вам нужно добавить обычное объявление, не являющееся членом, чтобы сделать его видимым для такого поиска.

...