Нет соответствия для 'operator==' в GCC 12

Когда я скомпилировал приведенный ниже код с использованием GCC 12, я обнаружил ошибку с надписью "no match for 'operator=='", но код отлично компилируется с GCC 11.

#include <iostream>
#include <algorithm>

class EqualityChecker
{
public:
  template <typename T>
  static bool checkEquality(const T lhs, const T rhs)
  {
    return *lhs == *rhs;
  }
};

namespace Foo
{
  class ValueEntity
  {
  private:
    int val;

  public:
    int value() const { return val; }
  };
}

bool operator==(const Foo::ValueEntity &lhs, const Foo::ValueEntity &rhs)
{
  return lhs.value() == rhs.value();
}

int main()
{
  Foo::ValueEntity v1;
  Foo::ValueEntity v2;

  bool equal = EqualityChecker::checkEquality(&v1, &v2);
  std::cout << "equal: " << equal << std::endl;

  return 0;
}

Скомпилировать команду:

g++-12 --std=c++20 main.cpp

Вывод ошибки:

main.cpp: In instantiation of 'static bool EqualityChecker::checkEquality(T, T) [with T = Foo::ValueEntity*]':
main.cpp:36:46:   required from here
main.cpp:10:17: error: no match for 'operator==' (operand types are 'Foo::ValueEntity' and 'Foo::ValueEntity')
   10 |     return *lhs == *rhs;

Подробный результат компиляции можно проверить по адресу https://godbolt.org/z/9jnMbYWvn

Кажется, проблема связана с правилами ADL C++, но в чем причина разницы между разными версиями GCC? Каким должно быть правильное поведение?

Переместите класс EqualityChecker после оператора ==. Я чувствую, что в GCC 11 была ошибка, которая была исправлена ​​в GCC 12. Вы также можете попробовать другие компиляторы, чтобы убедиться в этом.

kiner_shah 30.08.2024 12:49

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

HolyBlackCat 30.08.2024 13:02

clang дает хорошую диагностику: 'operator==' should be declared prior to the call site or in namespace 'Foo'

Artyer 30.08.2024 13:07

@kiner_shah Это упрощенный воспроизводимый код из более крупного проекта, некоторые из этих кодов взяты из #include, и их нелегко изменить порядок.

Jonny H 30.08.2024 13:42

К вашему сведению: MSVC также принимает код.

user12002570 30.08.2024 14:04
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
7
5
302
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Вам необходимо поместить класс EqualityChecker под определением оператора == в файле.

Я не знаю, почему это сработало с GCC 11, я не думаю, что так должно было быть. GCC 12, вероятно, исправил ошибку.

Хотя вопрос «как это исправить» полезен, вопрос не об этом. Более применимо объяснение того, какое поведение является правильным и почему исправление решает эту проблему.

cigien 30.08.2024 13:07

«Я не думаю, что так должно было быть. GCC 12, вероятно, исправил ошибку…» — это не ответ. Это спекуляция.

user12002570 30.08.2024 14:54
Ответ принят как подходящий

В GCC была давняя ошибка, из-за которой обычный неквалифицированный поиск (а не поиск ADL) операторов в шаблонах выполнялся с точки создания экземпляра, а не с точки определения шаблона (как и должно быть). Это было исправлено в GCC 12.

Чтобы надежно найти перегрузки операторов, их всегда следует искать через ADL (который выполняется с момента создания экземпляра). Это работает только в том случае, если перегрузка оператора объявлена ​​в том же пространстве имен, что и один из типов в аргументах вызова, к которым она должна применяться. Итак, переместите operator== внутрь Foo.

Кстати, это не относится к перегрузкам операторов. Любой неквалифицированный вызов, который вы хотите, чтобы пользователь шаблона мог перегрузить для настройки, подчиняется тем же правилам. Набор кандидатов на перегрузку, не являющийся членом выражения оператора ==, определен для простого выполнения поиска, как если бы это был неквалифицированный вызов функции operator== с заданными операндами в качестве аргументов функции.

«с точки создания экземпляра, а не с точки определения шаблона (как должно быть)» — поэтому gcc12 правильно отклоняет этот код, потому что определение шаблона предшествует объявлению operator==, верно?

pptaszni 30.08.2024 13:57

@pptaszni Да. Все предыдущие версии или, по крайней мере, многие версии, выпущенные за несколько лет, делали это неправильно.

user17732522 30.08.2024 13:58

@user17732522 MSVC также примите код. Поэтому вы можете добавить стандартную ссылку.

user12002570 30.08.2024 14:03

@user12002570 user12002570 Да, я не знаю, почему MSVC ведет себя так даже в режиме, соответствующем стандарту, но это неправильно.

user17732522 30.08.2024 14:06

@user12002570 user12002570 Это обычные правила поиска, поскольку они применяются ко всем неполным именам при поиске operator== для формирования набора перегрузки. Поиск всегда осуществляется по определению, за исключением ADL для функций, для которого есть специальное правило в timsong-cpp.github.io/cppwp/n4950/….

user17732522 30.08.2024 14:12

@user17732522 Похоже, я нашел исходную ошибку (gcc.gnu.org/bugzilla/show_bug.cgi?id=51577). По истории это было исправлено в GCC 12.

Jonny H 30.08.2024 18:00

@user17732522 user17732522 Вам следует добавить ссылку на ошибку gcc.

user12002570 31.08.2024 15:14

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