4

Following What is the copy and swap idiom and How to provide a swap function for my class, I tried implementing the swap function like in the latter accepted answer option number 2 (having a free function that calls a member function) instead of the direct friendly free function in the former link.

However the following doesn't compile

#include <iostream>

// Uncommenting the following two lines won't change the state of affairs
// class Bar;
// void swap(Bar &, Bar &);
class Bar {
public:
  Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // (1)
  Bar(Bar const & b) : bottles(b.bottles) { enforce(); } // (1)

  Bar & operator=(Bar const & b) {
    // bottles = b.bottles;
    // enforce();
    // Copy and swap idiom (maybe overkill in this example)
    Bar tmp(b); // but apart from resource management it allows (1)
                // to enforce a constraint on the internal state
    swap(*this, tmp); // Can't see the swap non-member function (2)
    return *this;
  }

  void swap(Bar & that) {
    using std::swap;
    swap(bottles, that.bottles);
  }

  friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
    out << b.bottles << " bottles";
    return out;
  }

private:
  unsigned int bottles;
  void enforce() { bottles /=2; bottles *= 2; } // (1) -- Ensure the number of bottles is even
};

void swap(Bar & man, Bar & woman) { // (2)
  man.swap(woman);
}

int main () {
  Bar man (5);
  Bar woman;

  std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
  swap(man, woman);
  std::cout << "After  -> m: " << man << " / w: " << woman << std::endl;

  return 0;
}

I know that the copy and swap idiom is overkill here but it also allows one to enforce some constraint on the internal state through the copy constructor (1) (A more concrete example would be to maintain a fraction in reduced form). Unfortunately, this doesn't compile because the only candidate for (2) that the compiler sees is the Bar::swap member function. Am I stuck with the friend non-member function approach?

EDIT: Go to my answer below to see what I ended up with thanks to all the answers and comments on this question.

Community
  • 1
  • 1
green diod
  • 1,399
  • 3
  • 14
  • 29
  • In no way does your code follow the [successful solution](http://stackoverflow.com/a/3279550/366904) in GMan's answer. – Cody Gray - on strike Mar 15 '16 at 12:11
  • 1
    if you correctly implement a nothrow move-assignment operator and move-constructor there is no need to implement swap. – Richard Hodges Mar 15 '16 at 12:20
  • 1
    Within the class, why not just call `this->swap(other)`? – Yakk - Adam Nevraumont Mar 15 '16 at 15:05
  • 1
    If there was ever a good example of slide 50 from this slide deck (http://www.slideshare.net/ripplelabs/howard-hinnant-accu2014), this example is it. Default the copy constructor and copy assignment operator and you will get code that is optimal and correct. You can do so simply by not mentioning them. The compiler will implicitly declare and define optimal code. – Howard Hinnant Mar 16 '16 at 02:53
  • @HowardHinnant I liked the slides, thanks for sharing them, too bad there's no video of it. – green diod Mar 16 '16 at 08:10

5 Answers5

5

I take it we're post c++11?

In which case, the default implementation of std::swap will be optimal, provided we correctly implement the move-assignment operator and the move-constructor (ideally nothrow)

http://en.cppreference.com/w/cpp/algorithm/swap

#include <iostream>

class Bar {
public:
    Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // (1)
    Bar(Bar const & b) : bottles(b.bottles) {
        // b has already been enforced. is enforce necessary here?
        enforce();
    } // (1)
    Bar(Bar&& b) noexcept
    : bottles(std::move(b.bottles))
    {
        // no need to enforce() because b will have already been enforced;
    }

    Bar& operator=(Bar&& b) noexcept
    {
        auto tmp = std::move(b);
        swap(tmp);
        return *this;
    }

    Bar & operator=(Bar const & b)
    {
        Bar tmp(b); // but apart from resource management it allows (1)
        swap(tmp);
        return *this;
    }

    void swap(Bar & that) noexcept {
        using std::swap;
        swap(bottles, that.bottles);
    }

    friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
        out << b.bottles << " bottles";
        return out;
    }

private:
    unsigned int bottles;
    void enforce() {  } // (1)
};

/* not needed anymore
void swap(Bar & man, Bar & woman) { // (2)
    man.swap(woman);
}
*/
int main () {
    Bar man (5);
    Bar woman;

    std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
    using std::swap;
    swap(man, woman);
    std::cout << "After  -> m: " << man << " / w: " << woman << std::endl;

    return 0;
}

expected result:

Before -> m: 5 bottles / w: 0 bottles
After  -> m: 0 bottles / w: 5 bottles

EDIT:

For the benefit of anyone who is concerned about performance (e.g. @JosephThompson) allow me to allay your concerns. After moving the call to std::swap into a virtual function (to force clang to produce any code at all) and then compiling with apple clang with -O2, this:

void doit(Bar& l, Bar& r) override {
    std::swap(l, r);
}

became this:

__ZN8swapper24doitER3BarS1_:            ## @_ZN8swapper24doitER3BarS1_
    .cfi_startproc
## BB#0:
    pushq   %rbp
Ltmp85:
    .cfi_def_cfa_offset 16
Ltmp86:
    .cfi_offset %rbp, -16
    movq    %rsp, %rbp
Ltmp87:
    .cfi_def_cfa_register %rbp
    movl    (%rsi), %eax
    movl    (%rdx), %ecx
    movl    %ecx, (%rsi)
    movl    %eax, (%rdx)
    popq    %rbp
    retq
    .cfi_endproc 

See? optimal. The c++ standard library rocks!

Richard Hodges
  • 68,278
  • 7
  • 90
  • 142
  • Yes for c++11 (I use gcc's -std=c++14). In fact, I would be quite happy with the default operator= but for the extra constraint enforcement that I want on the internal state of the class. Of course, I agree with your comments about the necessity (or not) of enforce() in some of the special functions. Especially in the context of this example where the bottles would go through the roof rather quickly! – green diod Mar 15 '16 at 12:49
  • However, if Bar inherited from Foo and if I had something like this `Bar(Foo const &)` (tell me if it's bad design) maybe, I'd like to make sure that enforce() is still called e.g. `Bar(Foo const & f) : Foo(f) { enforce(); }` – green diod Mar 15 '16 at 12:54
  • sure, that's fine. It's just that if you're constructing from a Bar, then in order to exist, that Bar *must* be valid, since it will have passed its own `enforce` check. so the call to `enforce` in the copy constructor is un-necessary (assuming enforce throws on failure, as it should) – Richard Hodges Mar 15 '16 at 12:56
  • Thinking about it, if I just constructed from the same class, I would just leave out all of these functions bar the direct initialization constructor which would be the only one enforcing the constraint on internal state. But thanks for unrolling all of the 4 and a half functions, I think they are pretty much useful if I *also* constructed from inherited classes. – green diod Mar 15 '16 at 13:08
  • If moving an object is exactly the same as copying it, then `std::swap` will be optimally efficient. If not, then I think you can usually do better with a custom `swap`. In your example, I think `std::swap` performs 9 integer copies. You could get that down to 5 if a moved-from `Bar` had zero bottles. If a moved-from `Bar` was left unmodified (i.e. move == copy), you would get the optimal 3 integer copies. – Joseph Thomson Mar 15 '16 at 13:39
  • @RichardHodges I changed `enforce` so that all Bar tenders have bottles as multiple of 2. Now, applying more than once wouldn't change bottles anymore. Of course you remark is still valid. – green diod Mar 15 '16 at 13:55
  • @JosephThomson I don't completely understand your move == copy. Could you elaborate on it? – green diod Mar 15 '16 at 13:56
  • @JosephThomson not since c++11. If the standard library detects that you have a move-constructor and a move-assignment operator, then std::move implements itself in terms of these. – Richard Hodges Mar 15 '16 at 13:58
  • @RichardHodges Yes, so `std::swap(a, b)` will move construct `a` into a temporary, move assign `b` into `a`, and then move assign the temporary into `b`. That is one move construction and two move assignments. Your move constructor makes one copy of `bottles`, while your move assignment operator makes four copies of `bottles`. This is a total of nine integer copies, which is less efficient than just calling `a.swap(b)`, which will perform three integer copies. – Joseph Thomson Mar 15 '16 at 14:05
  • @greendiod If moving `Bar` were the same as copying `Bar`, then `std::swap` would essentially perform three copies of `Bar`, which would be optimally efficient (three integer copies) in this case. – Joseph Thomson Mar 15 '16 at 14:08
  • @JosephThomson in unoptimised code, yes. I concede the point. – Richard Hodges Mar 15 '16 at 14:15
  • @RichardHodges Even in optimized code, if moving is not the same as copying, you can probably implement a custom `swap` which is more optimal than the default implementation of `std::swap`. That is, unless the compiler can optimize away the unnecessary operations (quite probable in this simple case). – Joseph Thomson Mar 15 '16 at 14:19
  • @RichardHodges Sure, for this simple example the compiler can completely optimize the `std::swap` operation, but it can't in the general case. As an example, try making `bottles` a `std::unique_ptr`. `Bar::swap` is still just four move instructions, whereas `std::swap` even calls `operator delete`. – Joseph Thomson Mar 15 '16 at 16:17
  • @JosephThomson Do you count the copies or the moves? How do you count them? Maybe you have a blog about that? Any link would be greatly appreciated. – green diod Mar 16 '16 at 08:35
  • 1
    @greendiod I'm talking about the `movl` assembly instructions, not C++ move operations. Copy [this code](https://gist.github.com/hpesoj/815fcb971cd71ba17649) into http://gcc.godbolt.org and compile with `-std=c++14 -O2` (Clang or GCC), then check the assembly for `std_swap` and `custom_swap`. On reflection, `operator delete` obviously isn't called at any point during the swap, but my point still stands that the compiler fails to generate "optimal" code. Of course, I'm just arguing from a technical standpoint ;). Please benchmark before you waste your time on premature optimization. – Joseph Thomson Mar 16 '16 at 10:31
  • 1
    @JosephThomson perhaps I should have written "close to optimal" – Richard Hodges Mar 16 '16 at 10:37
  • 1
    @RichardHodges Haha, yes. I agree that post-C++11 `std::swap` is good enough in the vast majority of cases. Unless you are writing highly optimized library code, or have benchmarks to back you up, writing a custom `swap` in addition to move operations is undoubtedly premature optimization. I was just trying to be technically correct: the best kind of correct :) – Joseph Thomson Mar 16 '16 at 10:41
  • @JosephThomson I didn't get it that you were going at a lower level even if RichardHodges update should have pointed me in that direction :P Anyway, it's really interesting so thanks again! – green diod Mar 16 '16 at 11:40
4

Note: This is pre C++11 way to use copy and swap. For a C++11 solution see this answer

In order to get this to work you need to fix a couple things. First you need to forward declare the swap free function so that operator= knows about it. In order to do that you also need to forward declare Bar so swap that there is a type named bar

class Bar;

void swap(Bar & man, Bar & woman);

// rest of code

Then we need to tell the compiler where to look for swap. The way we do that is to use the scope resolution operator. This will tell the compile to look in the out scope of the class for a swap function

Bar & operator=(Bar const & b) {
  // bottles = b.bottles;
  // enforce();
  // Copy and swap idiom (maybe overkill in this example)
  Bar tmp(b); // but apart from resource management it allows (1)
            // to enforce a constraint on the internal state
  ::swap(*this, tmp); // Can't see the swap non-member function (2)
//^^ scope operator 
  return *this;
}

We put all of that together and we get this Live Example

Really though the copy operator = should look like

Bar & operator=(Bar b) // makes copy
{
    ::swap(*this, b) // swap the copy
    return *this; // return the new value
}
Community
  • 1
  • 1
NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 1
    ...although this answer is closing the stable door after the horse has bolted isn't it? why not just use the member function swap inside member functions? – Richard Hodges Mar 15 '16 at 12:19
  • 1
    @RichardHodges The OP seems to not want to have a member function or a friend function. It looks like he is using option 2 from [this](http://stackoverflow.com/a/6380882/4342498) answer. I don't know which way is actually better or if you can say one way is better than the other so I presented this to get it to work. – NathanOliver Mar 15 '16 at 12:21
  • @NathanOliver Sorry, I meant bottles *= 2, it's a bit contrived here but enforce() would be reduce() in a Fraction class maintained in reduced form. – green diod Mar 15 '16 at 12:27
  • @greendiod OK. I fixed it in the live example so it compile now. – NathanOliver Mar 15 '16 at 12:28
  • @NathanOliver Ok, I had the forward declaration but lacked the scope operator. I also like your overloaded operator= where b is passed by value to ensure calling the constraint enforcing copy constructor! – green diod Mar 15 '16 at 12:31
  • @NathanOliver understood, but implementing an ADL swap is a bit oldskool these days. Since c++11 it's unnecessary if the class is move-assignable and move-constructible. Answer below. – Richard Hodges Mar 15 '16 at 12:44
  • 1
    @RichardHodges I think my edit will help to make that clear. – NathanOliver Mar 15 '16 at 12:52
1

You know that Bar has a swap member function, so just call it directly.

Bar& operator=(Bar const& b) {
    Bar tmp(b);
    tmp.swap(*this);
    return *this;
}

The non-member swap only exists so that clients of Bar can take advantage of its optimized swap implementation without knowing whether it exists, using the using std::swap idiom to enable argument-dependent lookup:

using std::swap;
swap(a, b);
Joseph Thomson
  • 9,888
  • 1
  • 34
  • 38
  • Do you mean that inside Bar, I should just use the `swap` member function while for clients of Bar, I should provide the non-member `swap` ? – green diod Mar 15 '16 at 12:42
  • 2
    Yes. Calling the non-member `swap` from within your class seems a bit pointless, as it ends up calling the member `swap` anyway. The non-member swap is just to support the `using std::swap` idiom for enabling ADL. It's particularly useful in generic code where you are swapping objects of unknown type. – Joseph Thomson Mar 15 '16 at 12:46
0

You need to enable std::swap in that function too.

using std::swap;
swap(*this, tmp); // Can't see the swap non-member function (2)

Quoting the answer you referred to:

If swap is now used as shown in 1), your function will be found.

The way it is used:

{
  using std::swap; // enable 'std::swap' to be found
                   // if no other 'swap' is found through ADL
  // some code ...
  swap(lhs, rhs); // unqualified call, uses ADL and finds a fitting 'swap'
                  // or falls back on 'std::swap'
  // more code ...
}

Live on Coliru

hlscalon
  • 7,304
  • 4
  • 33
  • 40
0

For the above context, where one only needs to enforce some internal constraint, better use the default and just enforce only once the constraint in the direct initialization constructor. Still if you need to implement these functions, look at @RichardHodges answer! See also @HowardHinnant comment (especially the slides part on when the compiler does magic implicitly declares special members ...).

Here's what I ended up with (no explicit copy and swap anymore):

#include <iostream>

class Bar {
public:
  Bar(unsigned int bottles=0) : bottles(bottles) { enforce(); } // The only point of enforcement

  friend std::ostream & operator<<(std::ostream & out, Bar const & b) {
    out << b.bottles << " bottles";
    return out;
  }

private:
  unsigned int bottles;
  void enforce() { bottles /= 2; bottles *=2; }
};

int main () {
  Bar man (5);
  Bar woman;

  std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
  using std::swap; // Argument dependent lookup
  swap(man, woman);
  std::cout << "After  -> m: " << man << " / w: " << woman << std::endl;

  return 0;
}

Now, what happens if Bar inherits from Foo (which doesn't need enforce). This is the original use case that made me think I needed to unroll my own special functions and to profit from the copy part of the copy and swap idiom to enforce the constraint. It turns out that even in this case I don't need to:

#include <iostream>

class Foo {
public:
  Foo(unsigned int bottles=11) : bottles(bottles) {} // This is odd on purpose

  virtual void display(std::ostream & out) const {
    out << bottles << " bottles";
  }

protected:
  unsigned int bottles;
};

std::ostream & operator<<(std::ostream & out, Foo const & f) {
  f.display(out);
  return out;
}

class Bar : public Foo {
public:
  Bar(unsigned int bottles=0) : Foo(bottles) { enforce(); }
  Bar(Foo const & f) : Foo(f) { enforce(); }

  void display(std::ostream & out) const override {
    out << bottles << " manageable bottles";
  }

private:
  void enforce() { bottles /= 2; bottles *=2; }
};

int main () {
  Bar man (5); // Again odd on purpose
  Bar woman;

  std::cout << "Before -> m: " << man << " / w: " << woman << std::endl;
  using std::swap; // Argument dependent lookup
  swap(man, woman);
  std::cout << "After  -> m: " << man << " / w: " << woman << std::endl;

  Foo fool(7); // Again odd
  Bar like(fool);
  std::cout << fool << " -> (copy) " << like << std::endl;
  Bar crazy;
  crazy = fool;
  std::cout << fool << " ->   (=)  " << crazy << std::endl;

  return 0;
}
green diod
  • 1,399
  • 3
  • 14
  • 29