Are function objects treated differently from regular functions during overload resolution? If so, how?
I have run into the following case where replacing a function with an equivalently-callable function object changed the meaning of the code:
#include <iostream>
namespace N
{
enum E { A, B };
void bar(E mode) { std::cout << "N::bar\n"; }
}
template <typename... Args>
void bar(Args&&... args) { std::cout << "global bar\n"; }
int main()
{
bar(N::A);
}
Here the output is "N::bar". So far, so good: N::bar is being found by ADL, both N::bar and the global bar are exact matches, and N::bar is preferred because it's not a template.
But if I change the global bar to be a function object, like so:
#include <iostream>
namespace N
{
enum E { A, B };
void bar(E mode) { std::cout << "N::bar\n"; }
}
struct S
{
template <typename... Args>
void operator()(Args&&... args) { std::cout << "global bar\n"; }
};
S bar;
int main()
{
bar(N::A);
}
The output is now "global bar". Why the difference?
The important bit here is that ADL only kicks in if lookup determines that the name is a function in the function call. In the second case, bar
is found to be an object and not a function, so the expression bar(N::A)
is not a function call, but the application of operator()
to the object bar
. Because it is not a function call, ADL does not kick in and N::bar
is not considered.
3.4.1/3
The lookup for an unqualified name used as the postfix-expression of a function call is described in 3.4.2. [ Note: For purposes of determining (during parsing) whether an expression is a postfix-expression for a func- tion call, the usual name lookup rules apply. The rules in 3.4.2 [ADL] have no effect on the syntactic interpretation of an expression.
Another way to look at it is to notice that ADL will add new functions to the set of overloaded functions, but in the second example there is no such set: lookup finds an object and a member of the object is called.