Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Name lookup for overloaded functions defined later

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?

like image 203
Travis Gockel Avatar asked Nov 03 '22 19:11

Travis Gockel


1 Answers

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 ::.

like image 115
Balog Pal Avatar answered Nov 08 '22 09:11

Balog Pal