3

I'm implementing a class with a similar interface to std::array, which has both the member swap() and the non-member swap().

Since I want my class to mimic the standard containers, I would like to implement both kinds of swap() (the non-member swap() is implemented via ADL, since specializing std::swap() isn't permitted):

class A {
    public:
        friend void swap(A& a, A& b) { /* swap the stuff */ }
        void swap(A& other) { swap(*this, other); }
};

However, it seems like I can't call the non-member swap() from inside the class, because it prefers the member swap() even though it only has a single parameter. Changing it to ::swap(*this, other) doesn't work as well, because the in-class friend function is only findable via ADL. How might I call the non-member swap() from inside the class?

Bernard
  • 5,209
  • 1
  • 34
  • 64
  • 5
    Why not to implement the non-member `swap` in terms of the member `swap`? – Evg Jan 02 '20 at 13:28
  • @Evg I could, but the symmetry of the non-member `swap` looks nicer. – Bernard Jan 02 '20 at 13:29
  • Why do you want to add the implementation of the swapping in the friend function `void swap(A& a, A& b)`? It should belong to a member function like `void swap(A& other)` and the `void swap(A& a, A& b)` then could call the member function. – t.niese Jan 02 '20 at 13:44
  • 1
    Also, if you do it the proper way (implement the non-member using the member), you don't need that the non member would be a friend of the class. **You are clearly inventing problems...** – Phil1970 Jan 02 '20 at 14:00
  • @Phil1970 Do you have a reference that says that implementing the non-member using the member is the proper way? – Bernard Jan 03 '20 at 07:22
  • Yes, you just have to look at **how it is done by experts in standard libraries**. With MSVC, this is done that way for `std::vector` and `std::array` and probably most other containers. Also you struggle to make it works that way while the other way is trivial to implement. I don't have specific references. After so many years, it become common knowledge. – Phil1970 Jan 03 '20 at 14:43

3 Answers3

4

The problem is that the name of the member function swap hides the namespace-scope swap in the body of A::swap. Unqualified name lookup for swap in A::swap will never find namespace-scope swap and, thus, namespace-scope swap will not be part of the overload set. One way to get around this would be to simply add a declaration for namespace-scope swap in the body of A::swap:

class A
{
public:
    friend void swap(A& a, A& b) { /* swap the stuff */ }

    void swap(A& other)
    {
        void swap(A& a, A& b);
        swap(*this, other);
    }
};

That being said, I'm not sure what this really does for you. The obvious solution is to just implement namespace-scope swap in terms of A::swap rather than the other way around. Personally, I would just not have a swap member function to begin with. The typical way of swapping aand b is to just swap(a, b)

Michael Kenzel
  • 15,508
  • 2
  • 30
  • 39
2

The following works for me (it prints blah twice)

#include <utility>
#include <iostream>

class A {
    public:
        friend void swap(A& a, A& b) { std::cout << "blah\n";/* swap the stuff */ }
        void swap(A& other) {  using std::swap; swap(*this, other); }
};

int main() {
    A a, b;
    swap(a,b);
    a.swap(b);
}
n314159
  • 4,990
  • 1
  • 5
  • 20
0

You can declare your class and your swap function first, then use the global namespace specifier within your member function.

Now you are free to define your swap function outside the class.

#include <algorithm>
#include <iostream>

class A;
void swap(A& a, A& b);

class A {
    int the_stuff;

  public:
    A(int a): the_stuff(a) {}
    friend void swap(A& a, A& b);
    void print_stuff(){std::cout << "A's stuff is " << the_stuff << std::endl;}
    void swap(A& other){
      ::swap(*this, other);
      std::cout << "Member swap" << std::endl;}
};

void swap(A& a, A& b)
{
  std::swap(a.the_stuff, b.the_stuff);
  std::cout << "Friend swap" << std::endl;
}

int main()
{
  A a = 1, b = 2;
  a.print_stuff();
  swap(a, b);
  a.print_stuff();
  return 0;
}

Outputs:

//> A's stuff is 1
//> Friend swap
//> A's stuff is 2
Allan Cameron
  • 147,086
  • 7
  • 49
  • 87
  • Note that friend `swap` in the question can benefit from being a [hidden friend](https://www.justsoftwaresolutions.co.uk/cplusplus/hidden-friends.html). – Evg Jan 02 '20 at 14:46