Это называется ADL, или поиск, зависящий от аргумента.
Для операторов компилятор будет искать не только в текущем пространстве имен подходящую функцию, но и в пространствах имен аргументов.
Например:
int main() {
int arr[3] = {};
std::vector<int> vec(3);
auto b_vec = begin(vec); // std::begin
auto b_arr = begin(arr); // Error!
}
При вызове begin
с помощью vec будет выполняться поиск в пространстве имен std
, поскольку std::vector
находится в этом пространстве имен. Для необработанного массива функция не найдена, поскольку у нее нет пространства имен, связанного с этим типом.
Один из способов отключить ADL - просто определить функцию:
// calls the std:: one, not the boost:: one
std::begin(some_boost_container);
Так же работает функция друга:
struct test {
friend void find_me(int) {}
};
find_me(3); // uh? no matching function?
Несмотря на то, что функция в полном порядке, ее невозможно найти.
Ему нужно имя класса внутри аргумента, чтобы ADL включился и нашел его в области видимости класса:
struct test {
friend void find_me(test const&) {}
};
find_me(test{}); // works!
Так ... для операторов? почему это работает?
потому что вызов пользовательского оператора примерно эквивалентен этому:
// arg1 == arg2;
operator==(arg1, arg2);
Поскольку имя функции не определено, ADL активируется. Затем найдите оператора в нужном пространстве имен и также можете найти дружественные функции.
Это позволяет использовать хороший синтаксис, а также позволяет универсальной функции вызывать функцию внутри вашего пространства имен.
Так почему же вектор не может найти тот в глобальном пространстве имен?
Это потому, что работает ADL. Внутри пространства имен с функцией с именем ADL будет выполнять поиск только внутри пространства имен аргумента. Но если его не найти, он вернется к обычному поиску.
namespace Cool {
struct Person {
std::string name;
};
}
bool cant_you_find_me(Cool::Person const& p);
namespace test {
void cant_you_find_me();
template<typename T>
void test_template(T const& p) {
cant_you_find_me(p); // ADL?
}
}
В этом примере ADL будет искать функцию cant_you_find_me
, но если ее невозможно найти через ADL, глобальное пространство имен не будет учитываться, так как обычный поиск найдет самое близкое: тот, который не имеет аргументов.
Вот что происходит с пространством имен std
. Он имеет много operator==
, определенных в нем. Если ADL не найдет подходящего, глобальное пространство имен не будет учитываться, а вместо этого в std
.