Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Comparison operator for std::vector<T> fails to find comparison operator for T

The following very simple code won't compile

#include <vector>
#include <string>


namespace Foobar {
    struct Test {
        std::string f;
        std::uint16_t uuid;
    };
}

bool operator==(const Foobar::Test& lhs, const Foobar::Test& rhs){
    return lhs.f == rhs.f && lhs.uuid == rhs.uuid;
}


int main(){

    std::vector<Foobar::Test> a;
    std::vector<Foobar::Test> b;

    if(a==b){

    }

    return 0;
}

https://godbolt.org/g/zn6UgJ

Won't compile in any of the compilers I have.

While the following

#include <vector>
#include <string>


namespace Foobar {
    struct Test {
        std::string f;
        std::uint16_t uuid;
    };

    bool operator==(const Foobar::Test& lhs, const Foobar::Test& rhs){
        return lhs.f == rhs.f && lhs.uuid == rhs.uuid;
    }
}



int main(){

    std::vector<Foobar::Test> a;
    std::vector<Foobar::Test> b;

    if(a==b){

    }

    return 0;
}

https://godbolt.org/g/o4pc1b

Compiles just fine, which makes me think std::vector<T> comparison operator looks in the namespace of T, why won't it consider the global namespace?

like image 872
arynaq Avatar asked Jul 20 '18 17:07

arynaq


1 Answers

Ordinary unqualified name lookup starts looking in the context where the name is used, and walks up the chain of enclosing scopes. It stops in the most nested scope that contains the matching name. This is true even if the name thus found is later determined to be unsuitable (e.g. the function overload is non-viable for a given call; or the member function is inaccessible).

Here, the context of the lookup is std::operator==(vector, vector), so it starts looking in namespace std. There are plenty of overloads of operator== in namespace std, so the ordinary lookup stops there and never reaches the global namespace.

In the second example, the overload is found by argument-dependent lookup. This lookup is performed specifically for function names in function calls, in addition to unqualified lookup, and looks for names in scopes associated with the types of the call's arguments. In the example, namespace Foobar is associated with Foobar::Test, and so argument-dependent lookup searches that namespace and finds Foobar::operator==.

For this reason, free functions that are logically part of the class' public interface - e.g. overloaded operators - should generally be defined in the same namespace as the class itself, to give argument-dependent lookup a chance to work. std::operator==(vector, vector) is a good example of that - a==b in your example works by way of argument-dependent lookup.

like image 188
Igor Tandetnik Avatar answered Oct 16 '22 05:10

Igor Tandetnik