1

Why can't I have a non-member function with the same name as a member function, if it also happens to take a pointer to the class?

This doesn't compile:

struct FooConfig{ int value; };
struct BarConfig{ int value; };

class Api
{
    void configure(const FooConfig& cfg);
    void configure(const BarConfig& cfg);
};

// helper
static void configure(Api* self, int value)
{
    // Actual impl
}

void Api::configure(const FooConfig& cfg)
{
    configure(this, cfg.value);
}

void Api::configure(const BarConfig& cfg)
{
    configure(this, cfg.value);
}

Both Gcc and Clang are lying to me, saying that there is no function named configure that takes 2 arguments.

And here comes the funny part:

If I just rename the helper function with the self pointer, or make it take a reference instead of pointer, then it suddenly comes to existence, and all is well.

AFAIK, the coincidental resemblence to a member function isn't supposed to matter in name lookup. Am I fooling C++?

user2394284
  • 5,520
  • 4
  • 32
  • 38
  • 3
    `::configure(this, cfg.value);` – Piotr Skotnicki May 16 '20 at 13:30
  • Wow! I wish the compiler would have told me that instead. I would accept that as an answer. – user2394284 May 16 '20 at 13:32
  • Overloading applies to functions defined **in the same scope**. `API` has two member functions name `configure`, and they are overloaded. That other thing in the global namespace is not defined in the same scope as the other two, so the compiler doesn't look for it. The compiler might be able to guess that you meant to use the global one, but you'd find a warning about that awfully annoying when there are half a dozen global functions, or when you don't intend to use the global one and simply mis-typed one of the arguments. – Pete Becker May 16 '20 at 14:56

3 Answers3

1

Try

::configure(this, cfg.value);
Ardent Coder
  • 3,777
  • 9
  • 27
  • 53
F.Guerinoni
  • 277
  • 2
  • 12
1

When trying to call configure in the member function, the name configure is found at class scope (i.e. the member functions), then name lookup stops, the further scope (the globle scope) won't be checked; the global configure can't be found, it's hidden by the member functions.

name lookup examines the scopes as described below, until it finds at least one declaration of any kind, at which time the lookup stops and no further scopes are examined.

As the workaround you can specfity the global one as ::configure(this, cfg.value);.

songyuanyao
  • 169,198
  • 16
  • 310
  • 405
1

Before overload resolution selects the best viable function, a set of candidate functions is formed, specifically matching the name of the callee. For that, the name lookup procedure is used.

Expression configure(this, cfg.value) is an unqualified call inside a class member function scope, and for that, the name lookup procedure will first search for the declaration in that function block itself (before the first use), and if not found, then traverse the class and its base classes, and only if still not found, it will visit the enclosing namespaces. That is, it will stop at the first declaration when using this ordered hierarchy of searched scopes.

In phase 2 of the above procedure, the name loopkup procedure finds both overalods of Api::configure, and that forms the candidate set for overload resolution. Since neither takes two parameters, the compiler correctly diagnoses an error.

In order to force the usage of the function from the global namespace, use ::configure(this, cfg.value). This indicates which namespace should be searched for for the declaration of configure.

If I [...] make it take a reference instead of pointer, then it suddenly comes to existence, and all is well.

That is not possible, as this does not impact the name lookup procedure. Changing the name, however, does.

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160