2013-07-02 9 views
4

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

#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 s, объявленный позже в блоке перевода?

+0

Я считаю, что второе определение должно быть * не * найдено в любом случае. Определения функций, не видимые из объявления шаблона, рассматриваются только в случае ADL, который здесь не встречается. –

+0

Во втором случае 'operator <<' вводится через ADL, потому что 'Foo' вводит пространство имен' :: '. –

ответ

2

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

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

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

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

+0

Ну, это сложная, но очень хорошая новость для меня: кто-то определяет 'struct Baz {friend void foo (const Baz & b);}' будет работать правильно, так как ADL найдет 'foo (const Baz &)'. –