Я заметил странное поведение в отношении поиска функций, когда полагался на функцию, которая будет определена позже:
#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 объявлен позже в переводческой единице?
Добро пожаловать в мир самых известных двухфазных правил поиска и странных правил.
Я уверен, что нет различий в операторах и функциях только за секунду, когда вы использовали еще один аргумент. Попробуйте, что произойдет, если для первой версии вы также добавите еще один параметр со структурой Foo …
Двухфазный поиск означает, что для зависимых имен, когда шаблон компилируется, он просматривает и запоминает набор видимых функций. В вашем случае это ничего не находит. Затем в контексте создания существует другой поиск, следуя правилам ADL (поиск, зависящий от аргумента). Только это. Это означает сначала сбор «связанных пространств имен» аргументов, а затем поиск дополнительных кандидатов в этих пространствах имен.
В вашем случае единственным аргументом является int, и он не имеет связанных пространств имен, поэтому ничего не найдено снова. Во втором случае у вас также есть Foo, который перетаскивает :: с ним, и ваш оператор находится в ::.
Других решений пока нет …