Когда я скомпилировал приведенный ниже код с использованием 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? Каким должно быть правильное поведение?
Перегруженные операторы должны находиться в том же пространстве имен, что и один из операндов. Тогда ADL сможет их найти, и порядок объявления не имеет значения.
clang дает хорошую диагностику: 'operator==' should be declared prior to the call site or in namespace 'Foo'
@kiner_shah Это упрощенный воспроизводимый код из более крупного проекта, некоторые из этих кодов взяты из #include, и их нелегко изменить порядок.
К вашему сведению: MSVC также принимает код.
Вам необходимо поместить класс EqualityChecker под определением оператора == в файле.
Я не знаю, почему это сработало с GCC 11, я не думаю, что так должно было быть. GCC 12, вероятно, исправил ошибку.
Хотя вопрос «как это исправить» полезен, вопрос не об этом. Более применимо объяснение того, какое поведение является правильным и почему исправление решает эту проблему.
«Я не думаю, что так должно было быть. GCC 12, вероятно, исправил ошибку…» — это не ответ. Это спекуляция.
В GCC была давняя ошибка, из-за которой обычный неквалифицированный поиск (а не поиск ADL) операторов в шаблонах выполнялся с точки создания экземпляра, а не с точки определения шаблона (как и должно быть). Это было исправлено в GCC 12.
Чтобы надежно найти перегрузки операторов, их всегда следует искать через ADL (который выполняется с момента создания экземпляра). Это работает только в том случае, если перегрузка оператора объявлена в том же пространстве имен, что и один из типов в аргументах вызова, к которым она должна применяться. Итак, переместите operator==
внутрь Foo
.
Кстати, это не относится к перегрузкам операторов. Любой неквалифицированный вызов, который вы хотите, чтобы пользователь шаблона мог перегрузить для настройки, подчиняется тем же правилам. Набор кандидатов на перегрузку, не являющийся членом выражения оператора ==
, определен для простого выполнения поиска, как если бы это был неквалифицированный вызов функции operator==
с заданными операндами в качестве аргументов функции.
«с точки создания экземпляра, а не с точки определения шаблона (как должно быть)» — поэтому gcc12 правильно отклоняет этот код, потому что определение шаблона предшествует объявлению operator==
, верно?
@pptaszni Да. Все предыдущие версии или, по крайней мере, многие версии, выпущенные за несколько лет, делали это неправильно.
@user17732522 MSVC также примите код. Поэтому вы можете добавить стандартную ссылку.
@user12002570 user12002570 Да, я не знаю, почему MSVC ведет себя так даже в режиме, соответствующем стандарту, но это неправильно.
@user12002570 user12002570 Это обычные правила поиска, поскольку они применяются ко всем неполным именам при поиске operator==
для формирования набора перегрузки. Поиск всегда осуществляется по определению, за исключением ADL для функций, для которого есть специальное правило в timsong-cpp.github.io/cppwp/n4950/….
@user17732522 Похоже, я нашел исходную ошибку (gcc.gnu.org/bugzilla/show_bug.cgi?id=51577). По истории это было исправлено в GCC 12.
@user17732522 user17732522 Вам следует добавить ссылку на ошибку gcc.
Переместите класс EqualityChecker после оператора ==. Я чувствую, что в GCC 11 была ошибка, которая была исправлена в GCC 12. Вы также можете попробовать другие компиляторы, чтобы убедиться в этом.