Costantino Grana
Costantino Grana

Reputation: 3418

Friend function and method with the same name

The following classes definitions declare a friend function providing an inline definition for it. I was trying to invoke the friend function from a class method with the same name of the friend function, but in order to make it work I have to access it from the enclosing namespace (which also requires a forward declaration, class C below). Why the name lookup works for class A and it doesn't work in class B? Note that the parameters of B::swap are different from those of its friend function.

#include <utility>

struct A {
    A(int x) : v{ x } {}
    friend void swap(A& x, A& y) { std::swap(x.v, y.v); }
    void swapm(A& other) { swap(*this, other); }
private:
    int v;
};

struct B {
    B(int x) : v{ x } {}
    friend void swap(B& x, B& y) { std::swap(x.v, y.v); }
    void swap(B& other) { swap(*this, other); } // <-- This doesn't compile
private:
    int v;
};

struct C;
void swap(C& x, C& y);
struct C {
    C(int x) : v{ x } {}
    friend void swap(C& x, C& y) { std::swap(x.v, y.v); }
    void swap(C& other) { ::swap(*this, other); }
private:
    int v;
};

int main()
{
    A a1{ 1 }, a2{ 2 }; swap(a1, a2); a1.swapm(a2);
    B b1{ 3 }, b2{ 4 }; swap(b1, b2); b1.swap(b2);
    C c1{ 5 }, c2{ 6 }; swap(c1, c2); c1.swap(c2);
}

Upvotes: 0

Views: 889

Answers (2)

jwismar
jwismar

Reputation: 12258

Whether it's a good idea or not, here's an explanation of why it's failing:

The compiler uses several different phases of processing to figure out what your program says. The reason that class B doesn't compile is because the failure that's occurring happens before the friend would be noticed. Let me explain:

When the compiler gets to the point where it's trying to figure out what swap means, it does a name lookup. It uses specific rules that specify where it should look. This is simplified, but basically it first looks for symbols defined in local scope, then in class scope, and then in enclosing (namespace, etc) scopes. It finds the one defined in class scope, and stops looking. That swap does not take those 2 parameters, so it the compilation fails.

The friend declaration, which allows the free function to access B's internals, acts as an additional declaration for the swap function that you've declared in the global namespace. These declarations would be considered by the compiler if it got to the point in name lookup where it was considering functions in the global namespace. In class B, the compiler has already stopped processing before it gets to this stage. (And the friend declaration would be necessary in an even later phase, when the compiler is working on compiling a version of the swap function that works with B objects, and wants to figure out, "in this function called swap that can take these 2 parameters; can I access B's internals?")

In class A, you're using a different name. The name lookup phase doesn't succeed until it locates your free swap function. In class C, you've given the name-lookup specific instructions, "hey, when you're looking up swap, look in global-namespace scope, and ignore the ones in local- and class-scope that you might find."

(Note: description of name-lookup and friend updated after @PeteBecker's comment.)

Upvotes: 4

Matthieu Brucher
Matthieu Brucher

Reputation: 22023

inline friend? Why not static in this case? And not friend? And then create a global one that calls the static. For me, this is a bad design more than an actual problem.

The method swap should be the one doing the work instead of the friend (because no need for a friend anymore):

struct C {
    C(int x) : v{ x } {}
    void swap(C& other) { std::swap(this->v, other.v); }
private:
    int v;
};

void swap(C& x, C& y)
{
  x.swap(y);    
}

Upvotes: 0

Related Questions