12

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?

HighCommander4
  • 50,428
  • 24
  • 122
  • 194

2 Answers2

12

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.

Community
  • 1
  • 1
David Rodríguez - dribeas
  • 204,818
  • 23
  • 294
  • 489
  • I don't think that the note at 3.4.1/3 is relevant, because `bar` *is* the postfix-expression in a function call expression (not having a function as operand, but eventually calling one!). The note says that 3.4.2 does not change the syntactic meaning of "x(y)" - if it is a function call, it stays a function call disregarding of what 3.4.2 decides to do. – Johannes Schaub - litb Aug 30 '12 at 21:31
  • @JohannesSchaub-litb: Regular lookup will find that `bar` is an object, and being an object `bar(x)` represents the application of `operator()` to that instance. While the application of the operator is a function call, it is a *member-function* call, and thus ADL does not apply. – David Rodríguez - dribeas Aug 30 '12 at 22:29
  • @david you are confusing all the different entities here – Johannes Schaub - litb Aug 31 '12 at 08:22
4

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.

Johannes Schaub - litb
  • 496,577
  • 130
  • 894
  • 1,212