no match for 'operator==' in GCC 12
Asked Answered
H

1

8

When I compiled the code below using GCC 12, I encountered an error saying "no match for 'operator=='", but the code compiles fine with 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;
}

Compile command:

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

Error output:

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;

Detail compilation result can be checked at https://godbolt.org/z/9jnMbYWvn

It seems the issue is related to C++'s ADL rules, but what causes the difference between different GCC versions? What is the correct behavior supposed to be?

Headword answered 30/8, 2024 at 10:47 Comment(5)
Move EqualityChecker class after operator==. I feel GCC 11 had a bug that was fixed in GCC 12. You can try other compilers as well to confirm this.Ger
Overloaded operators should be in same namespace as one of the operands. Then ADL can find them and the declaration order doesn't matter.Baalbek
clang gives a nice diagnostic for this: 'operator==' should be declared prior to the call site or in namespace 'Foo'Millham
@Ger This is a simplified reproducible code from a larger project, some of these code are from #include and not easy to reorder.Headword
FYI: MSVC also accept the code.Swink
N
8

GCC had a long-standing bug which caused the normal unqualified lookup (rather than the ADL lookup) of operators in templates to be performed from the point of instantiation instead of the point of definition of the template (as it should be). That has been fixed in GCC 12.

To reliably find operator overloads they should always be found via ADL (which is performed from the point of instantiation). That only works if the operator overload is declared in the same namespace as one of the types in the call arguments to which it shall apply. So, move operator== inside Foo.

This isn't specific to operator overloads btw. Any unqualified call that you want the user of a template to be able to overload for customization follows the same rules. The non-member overload candidate set of a == operator expression is defined to simply perform lookup as if for an unqualified operator== function call with the given operands as function arguments.

Nita answered 30/8, 2024 at 11:52 Comment(7)
"from the point of instantiation instead of the point of definition of the template (as it should be)" - so gcc12 is correct to reject this code, because template definition is before operator== declaration, right?Exon
@Exon Yes. All previous versions, or at least many versions spanning several years, did it incorrectly.Nita
@Nita MSVC also accept the code. So you might want to add a standard reference.Swink
@Swink Yes, I don't know why MSVC behaves like that even in standard-conforming mode, but it isn't correct.Nita
@Swink It is just the usual lookup rules as they apply to all unqualified names when looking for operator== to form the overload set. The lookup is always from the definition, except for ADL for functions, which has the special rule in timsong-cpp.github.io/cppwp/n4950/….Nita
@Nita It looks like I found the original bug (gcc.gnu.org/bugzilla/show_bug.cgi?id=51577). According to the history, it was fixed in GCC 12.Headword
@Nita You should add a link to the gcc bug.Swink

© 2022 - 2025 — McMap. All rights reserved.