Question

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?

Was it helpful?

Solution

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.

OTHER TIPS

See 3.4.2p3, which says

Let X be the lookup set produced by unqualified lookup (3.4.1) and let Y be the lookup set produced by argument dependent lookup (defined as follows). If X contains

  • ...
  • a declaration that is neither a function or a function template

then Y is empty.

If there would not be such a rule, you are right: ADL would add your other function to the overload set. In fact, 13.3.1.1p1 relies on this: It has two branches; one for function call expressions where the operand denotes a class object, and another one where the operand denotes one or more functions or function templates.

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