Поиск имени для перегруженных функций, определенных позже

Я заметил странное поведение в отношении поиска функций, когда полагался на функцию, которая будет определена позже:

#include <iostream>

template <typename T>
void foo(const T&)
{
    std::cout << "Basic" << std::endl;
}

template <typename T>
void bar()
{
    T x;
    foo(x);
}

void foo(const int& x)
{
    std::cout << "int:" << x << std::endl;
}

int main()
{
    bar<int>();
}

Выход:

Basic

По какой-то причине я ожидал использования foo внутри bar, чтобы найти перегрузку под ним. Перемещение перегрузки foo выше bar делает вывод желаемым int:0 (или просто написание объявления).

Такое же поведение не применимо к перегрузке бинарного оператора:

#include <iostream>

struct Foo {} foo;

template <typename T>
void operator<<(const Foo&, const T&)
{
    std::cout << "Basic" << std::endl;
}

template <typename T>
void bar()
{
    T x;
    foo << x;
}

void operator<<(const Foo&, const int& x)
{
    std::cout << "int:" << x << std::endl;
}

int main()
{
    bar<int>();
}

Выход:

int:0

У меня есть два вопроса, первый: почему такое поведение и почему оно отличается от перегрузки оператора? Второй: если у меня есть именованная функция (например, мое использование foo), есть ли способ написать функцию bar таким образом, чтобы обнаруживать перегруженные foo, объявленные позже в единице перевода?


person Travis Gockel    schedule 02.07.2013    source источник
comment
Я считаю, что второе определение не должно встречаться ни в том, ни в другом случае. Определения функций, невидимые из объявления шаблона, учитываются только в случае ADL, чего здесь не происходит.   -  person n. 1.8e9-where's-my-share m.    schedule 02.07.2013
comment
Во втором случае operator << вводится через ADL, потому что Foo вводит пространство имен ::.   -  person Travis Gockel    schedule 03.07.2013


Ответы (1)


Добро пожаловать в мир самого известного двухэтапного поиска и странных правил.

Я уверен, что нет никакой разницы в случаях оператора и функции, просто на секунду вы использовали еще один аргумент. Попробуйте, что произойдет, если для первой версии вы также добавите еще один параметр со структурой Foo...

Двухэтапный поиск означает, что для зависимых имен при компиляции шаблон просматривает и запоминает набор видимых функций. В вашем случае это ничего не находит. Затем в контексте инстанцирования выполняется еще один поиск по правилам ADL (поиск, зависящий от аргумента). Только это. Это означает сначала собрать «связанные пространства имен» аргументов, а затем искать других кандидатов в этих пространствах имен.

В вашем случае единственным аргументом является int, и у него нет связанных пространств имен, поэтому снова ничего не найдено. Во втором случае у вас также есть Foo, который тащит за собой ::, а ваш оператор находится в ::.

person Balog Pal    schedule 02.07.2013
comment
Ну, это сложная, но очень хорошая новость для меня: кто-то, определяющий struct Baz { friend void foo(const Baz& b); }, заставит его работать правильно, так как ADL найдет foo(const Baz&). - person Travis Gockel; 02.07.2013