Оператор сравнения для std :: vector <t> не удается найти оператор сравнения для T

Следующий очень простой код не будет компилироваться

#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

Не компилируется ни в одном из компиляторов, которые у меня есть.

В то время как следующие

#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

Компилирует просто отлично, что заставляет меня думать, что оператор сравнения std::vector<T> смотрит в пространство имен T, почему он не будет рассматривать глобальное пространство имен?

Ответ 1

Обычный неквалифицированный поиск имени начинает искать в контексте, где используется имя, и поднимает цепочку охватывающих областей. Он останавливается в самой вложенной области, содержащей совпадающее имя. Это верно, даже если имя, найденное таким образом, позднее определяется как неподходящее (например, функция перегрузки является нежизнеспособной для данного вызова или функция-член недоступна).

Здесь контекст поиска - std::operator==(vector, vector), поэтому он начинает искать в пространстве имен std. Существует много перегрузок operator== в пространстве имен std, поэтому обычный поиск останавливается и никогда не достигает глобального пространства имен.

Во втором примере перегрузка найдена с помощью зависящего от аргумента поиска. Этот поиск выполняется специально для имен функций в вызовах функций, в дополнение к неквалифицированному поиску, и ищет имена в областях, связанных с типами аргументов вызова. В примере пространство имен Foobar связано с Foobar::Test, и поэтому зависящий от аргумента поиск ищет это пространство имен и находит Foobar::operator==.

По этой причине свободные функции, которые логически являются частью открытого интерфейса класса, например перегруженные операторы, обычно должны быть определены в том же пространстве имен, что и сам класс, чтобы дать возможность зависящим от аргумента поиска работать. std::operator==(vector, vector) является хорошим примером этого: a==b в вашем примере работает в зависимости от зависимого от аргумента поиска.