C++ friend function hidden by class function?

喜欢而已 提交于 2019-12-05 01:36:05

I believe it is because the compiler is trying to find the function within the class. This should be a minimalistic change to make it work (it works in Visual Studio 2012):

class A; // this and the next line are not needed in VS2012, but
void swap(A& first, A& second); // will make the code compile in g++ and clang++

class A
{
    friend void swap(A& first, A& second) {}
    void swap(A& other) {}
    void call_swap(A& other)
    {
        ::swap(*this, other); // note the scope operator
    }
};

int main() { return 0; }

As a workaround, you can declare a static version of swap. Then, you can declare the friend version to call the static version.

class A
{
public:
    friend void swap(A& first, A& second) { A::swap(first, second); }
private:
    static void swap(A& first, A& second) {}
    void swap(A& other) {}
    void call_swap(A& other)
    {
        swap(*this, other);
    }
};

int main () {
    A a, b;
    swap(a, b);
}
Mike Seymour

Why

Inside the class, names scoped within the class hide those in the surrounding namespace; so the friend (whose name is scoped in the namespace, but not directly accessible there) is hidden by the member (scoped in the class) and not available as a potential overload here. (Update: or perhaps it's a bit more complicated than that, as mentioned in the comments. The scope and name lookup rules are a bit hard to follow, especially when friends are involved).

how to fix this, if I want to keep both variants of my swap function?

There's no perfect solution. If the functions both do the same thing, then just use the member from other member functions. If you declare the friend outside the class definition, then it's accessible as ::swap; but this is a bit fragile if you put the class in a different namespace. (Update: or use a static member function as suggested by this answer; I didn't think of that).

Keep to the standard swap idiom, and you won't have a problem:

void call_swap(A& other) {
  using std::swap;
  swap(*this, other);
}

Or use the Boost.Swap wrapper:

void call_swap(A& other) {
  boost::swap(*this, other);
}

This is pretty much equivalent to @Juan's solution, except you're not writing the helper yourself.

You can also use a helper function, as in this case

template<class T>
void swap_elems(T& lhs, T& rhs)
{
    using namespace std;
    swap(lhs, rhs);
}

class A
{
friend void swap(A& first, A& second) { first.swap(second); }

  public:
    void swap(A& other) {}
    void call_swap(A& other)
    {
        swap_elems(*this, other);
    }
};

What you're observing here is that in absence of a previous declaration of a friend function, friendship within a class injects the name into the enclosing namespace, but NOT into the class scope. The only thing that happens at class scope is that the function named is granted access to private attributes.

This leaves only one swap function in the class scope (the member with one parameter) so that's the only candidate overload. Once you've found a candidate even if overload resolution fails you will never try another enclosing scope (shadowing).

If you really need both versions (and step back to make sure you do), put the implementation into a function like swap_impl which you call from the friend and the member.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!