Mircea Ispas
Mircea Ispas

Reputation: 20780

Why calling a non-member function with the same name as a member function generates an error

I have next code:

void f(int){}

struct A
{
    void f()
    {
        f(1);
    }
};

This code is not well-formed with the error message (GCC): error: no matching function for call to ‘A::f(int)’ or (clang) Too many arguments to function call, expected 0, have 1; did you mean '::f'?

Why do I need to use :: to call the non-member function with the same name as the member function, but with different signature? What is the motivation for this requirement?

I think the compiler should be able to figure it out I want to call the non-member function as the signature is different (clang even puts that in the error message!).

Please don't mark this as duplicate - it is a different question from this Calling in C++ a non member function inside a class with a method with the same

Upvotes: 19

Views: 3927

Answers (4)

Mike Seymour
Mike Seymour

Reputation: 254461

Why do I need to use :: to call the non-member function with the same name as the member function, but with different signature

Because those are the rules. Names in a nested scope hide entities with the same name in a wider scope.

What is the motivation for this requirement?

Consider the case where a member function calls another member with a signature that doesn't quite match:

struct A {
    void f(double);
    void g() {f(42);}  // requires int->double conversion
};

Now suppose someone adds an unrelated function in the surrounding namespace

void f(int);

If this were included in the set of overloads within the scope of A, then suddenly the behaviour of A::g would change: it would call this instead of A::f. Restricting the overload set to names in the narrowest available scope prevents this kind of unexpected breakage.

As you (and your helpful compiler) say, the outer name is still available (with qualification) if you need it.

Upvotes: 24

legends2k
legends2k

Reputation: 32904

Why do I need to use :: to call the non-member function with the same name as the member function, but with different signature? What is the motivation for this requirement?

That is the whole point of having namespaces. A local (closer-scoped) name is preferred and more visible over a global name. Since a struct is again a scope, its f is shadowing the visibility of ::f. When you've to have the global one, you've to say you do. Why?

This is provided as a feature to make sure you can peacefully call functions you defined assuming they would get called, and when you need one from a different namespace, say the standard library, you'd state that explicitly, like std::. It's just a clean form of disambiguation, without leaving room for chance to play its part.

Upvotes: 1

Mr.C64
Mr.C64

Reputation: 42934

To understand the reason of your error and why you need to explicitly use the ::f() syntax, you may want to consider some aspects of the C++ compiler process:

The first thing the compiler does is name lookup.

Unqualified name lookup starts from the current scope, and then moves outwards; it stops as soon as it finds a declaration for the name of the function, even if this function will be later determined to not be a viable candidate for the function call.

Then, overload resolution is executed over the set of the functions that name lookup found.

(And, finally, access check is executed on the function that overload resolution picked up.)

Upvotes: 0

T.C.
T.C.

Reputation: 137330

The compiler performs unqualified name lookup, for f, specified in §3.4.1 [basic.lookup.unqual] (thankfully, there's no ADL here):

1 In all the cases listed in 3.4.1, the scopes are searched for a declaration in the order listed in each of the respective categories; name lookup ends as soon as a declaration is found for the name. If no declaration is found, the program is ill-formed.

8 For the members of a class X, a name used in a member function body, in a default argument, in an exception-specification, in the brace-or-equal-initializer of a non-static data member (9.2), or in the definition of a class member outside of the definition of X, following the member’s declarator-id, shall be declared in one of the following ways:

  • before its use in the block in which it is used or in an enclosing block (6.3), or
  • shall be a member of class X or be a member of a base class of X (10.2), or
  • if X is a nested class of class Y (9.7), shall be a member of Y, or shall be a member of a base class of Y (this lookup applies in turn to Y’s enclosing classes, starting with the innermost enclosing class), or
  • if X is a local class (9.8) or is a nested class of a local class, before the definition of class X in a block enclosing the definition of class X, or
  • if X is a member of namespace N, or is a nested class of a class that is a member of N, or is a local class or a nested class within a local class of a function that is a member of N, before the use of the name, in namespace N or in one of N’s enclosing namespaces.

Name lookup stops as soon as a declaration is found. So once it finds the member f() at the second bullet point it stops and never searches elsewhere.

Rejection of nonviable functions are done after name lookup, at overload resolution.

Upvotes: 8

Related Questions