Question

I noticed strange behavior regarding function lookup when relying on a function to be defined later:

#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>();
}

Output:

Basic

For some reason, I expected the use of foo inside bar to find the overload below it. Moving the overload of foo to above bar makes the output the desired int:0 (or just writing a declaration).

This same behavior does not appear to apply to overloading a binary operator:

#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>();
}

Output:

int:0

I have two questions, the first is: Why is the behavior like this and why is it different for operator overloading? The second is: If I have a named function (like my use of foo), is there a way to write a function bar in such a way to discover overloaded foos declared later in a translation unit?

Was it helpful?

Solution

Welcome to the world of the most famous two phase lookup, and weirdo rules.

I'm sure there is no difference on operator and function cases just for the second you used one more argument. Try what happens if for the first version you also add another parameter with struct Foo...

Two phased lookup means that for dependent names when the template is compiled, it looks around and remember the set of visible functions. In your case that finds nothing. Then in the instantiation context there is another lookup, following the ADL (argument-dependent lookup) rules. That only. It means first collecting "associated namespaces" of the arguments, then look for more candidates in those namespaces.

In your case the only argument is int, and it has no associated namespaces, so nothing is found again. In the second case you also have Foo that drags :: with it, and your operator is found in ::.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top