1

Consider the following example:

namespace X {
    struct A { static const A aaa; };
    const A A::aaa = {};
}

bool operator == (const X::A &a, const X::A &b) { return true; }
bool workaround (const X::A &a, const X::A &b) { return a == b; }

namespace Y {
    using X::A;

    struct B { const A & an_a() const { static A a; return a; } };

    bool operator == (const B &a, const B &b) { return true; }

    bool foo (const B &b) {
        return b.an_a() == A::aaa;
        //return workaround(b.an_a(), A::aaa);
    }
}

int main () {}

This code fails to compile foo because the == operator resolves the one in namespace Y rather than the one in the global namespace.

.code.tio.cpp:17:25: error: invalid operands to binary expression ('const X::A' and 'const X::A')
    return b.an_a() == A::aaa;
           ~~~~~~~~ ^  ~~~~~~
.code.tio.cpp:14:10: note: candidate function not viable: no known conversion from 'const X::A' to 'const Y::B' for 1st argument
    bool operator == (const B &a, const B &b) { return true; }
     ^
1 error generated.

I think I understand this is due to the way ADL has been defined for C++, which seems to explicitly exclude searching the global namespace if the class belongs to a namespace. But, it still seems counter-intuitive to me. Is there an explanation of why the global namespace could not be included in the set of namespaces to be searched for operator==?

jxh
  • 69,070
  • 8
  • 110
  • 193
  • Not a dupe but some explanation: https://stackoverflow.com/questions/8111677/what-is-argument-dependent-lookup-aka-adl-or-koenig-lookup – NathanOliver Feb 23 '18 at 01:16
  • I think it means that since `A` is not declared in the global namespace, operators in the global namespace are not considered. `foo` is part of namespace `Y`, and it is passing two `A` objects from namespace `X` to `operator==`, so the compiler is going to look only in namespaces `Y` and `X` for a matching operator, which it doesn't find. Also see [Argument-dependent lookup](http://en.cppreference.com/w/cpp/language/adlhttp://en.cppreference.com/w/cpp/language/adl) on cppreference.com for a more comprehensive list of the rules that ADL uses. – Remy Lebeau Feb 23 '18 at 01:20
  • Regular unqualified name lookup walks inside out until it finds a name; as soon as it finds that name, it stops. In your example, it finds `Y::operator==` and stops there, not looking at any enclosing scopes. ADL only looks at namespaces associated with the arguments - in your case, `X`; it doesn't walk up into enclosing scopes at all. – Igor Tandetnik Feb 23 '18 at 01:26
  • Name lookup always stops once it finds the name, without regard to whether overload resolution will succeed. This is not specific to ADL. – Brian Bi Feb 23 '18 at 01:28
  • You would have to ask Koenig, I suppose. I cannot testify to his state of mind. – Igor Tandetnik Feb 23 '18 at 01:28

1 Answers1

1

For consistency, operators are considered to be just function names, and a function hides any in an enclosing scope (even if the parameter types differ). (The latter rule is presumably intended to prevent unexpected overload resolution interaction between functions declared in different scopes).

Your ::operator==(const X::A&,const X::A&) is hidden (within Y) by Y::operator==(const B&,const B&), and it's not considered to be part of the interface of X::A (because it's not in the immediate namespace X), so you can't find it.

If you're willing to change the spelling of the comparison, you can use the ugly, explicit ::operator==(b.an_a(), A::aaa) instead of adding workaround.

Davis Herring
  • 36,443
  • 4
  • 48
  • 76