38

Possible Duplicate:
Move semantics == custom swap function obsolete?

This is how std::swap looks like in C++11:

template<typename T>
void swap(T& x, T& y)
{
  T z = std::move(x);
    x = std::move(y);
    y = std::move(z);
}

Do I still have to specialize std::swap for my own types, or will std::swap be as efficient as it gets, provided that my class defines a move constructor and a move assignment operator, of course?

Community
  • 1
  • 1
fredoverflow
  • 256,549
  • 94
  • 388
  • 662
  • I want to say this was mentioned by either Scott or Herb at _C++ and Beyond_, but I can't seem to find anything about it. – Michael Kristofik Dec 23 '11 at 14:59
  • 2
    Related: [Move semantics == custom swap function obsolete?](http://stackoverflow.com/questions/6416385/move-semantics-custom-swap-function-obsolete) – Xeo Dec 23 '11 at 16:31
  • @Xeo: Thanks, I had completely forgotten about that question/answer. Do I get points for being consistent? :-) I sure would've been red-faced otherwise! – Howard Hinnant Dec 23 '11 at 18:05
  • @Howard: We actually discussed if this one should be closed as a dupe, but decided not to because you really only touched the performance argument in my question and spent more of the answer to explain the `noexcept` stuff, while you explained the performance stuff in detail here. :) Btw, what did you mean with the footnote on page 502? Afaics, there is no footnote on that page. – Xeo Dec 23 '11 at 18:59
  • Search http://stackoverflow.com/questions/6416385/move-semantics-custom-swap-function-obsolete for "duck". It was just a small joke. – Howard Hinnant Dec 23 '11 at 20:36

4 Answers4

36

The specialization of std::swap is now optional, but not deprecated. The rationale is performance.

For prototyping code, and perhaps even for much shipping code, std::swap will be plenty fast. However if you're in a situation where you need to eek out every little bit from your code, writing a custom swap can still be a significant performance advantage.

Consider the case where your class essentially has one owning pointer and your move constructor and move assignment just have to deal with that one pointer. Count machine loads and stores for each member:

Move constructor: 1 load and 2 stores.

Move assignment: 2 loads and 2 stores.

Custom swap: 2 loads and 2 stores.

std::swap is 1 move construction and 2 move assignments, or: 5 loads and 6 stores.

A custom swap is potentially still two or three times faster than std::swap. Though any time you're trying to figure out the speed of something by counting loads and stores, both are going to be wicked fast.

Note: In computing the cost of your move assignment, be sure and take into account that you will be moving into a moved-from value (in the std::swap algorithm). This often negates the cost of a deallocation, though at the cost of a branch.

Howard Hinnant
  • 206,506
  • 52
  • 449
  • 577
  • 8
    Shouldn't it be possible for the compiler to inline the move assignments and remove dead stores? – u0b34a0f6ae Dec 23 '11 at 16:27
  • 8
    I'm not a compiler engineer. I would guess that's possible, but I don't know for sure. I would be hesitant to depend on it. At the end of the day you're going to have to code it both ways and test. And if your test indicates that `std::swap` is just as fast, that result may well be compiler/platform dependent. And if your application doesn't `swap` in a performance critical region, then you probably won't care if `std::swap` is twice as slow as wicked fast. – Howard Hinnant Dec 23 '11 at 17:57
  • 1
    Coming a bit late to the party... But I have a simple doubt: why is Move Assignment accounted for 2Loads + 2Stores (1 more Load than Move Constructor)? Is it because it has to do "return *this"? Or because of the implied need to "delete" the owning pointer before reassigning it, and so it has to be read in the delete expression? – abigagli Apr 05 '14 at 17:36
  • 2
    @abigagli: Both lhs and rhs pointers need to be loaded into registers. The lhs needs to be loaded so it can be inspected and perhaps deleted. The rhs pointer needs to be loaded into a register to store it to the lhs (unless your machine has memory-to-memory move operation). Both lhs and rhs pointers need a new value, thus 2 stores. – Howard Hinnant Apr 05 '14 at 23:56
  • @HowardHinnant Thanks for the reply, even after so much time. So yes, it is the access to lhs pointer to "inspect and perhaps delete" that makes it for the additional load with respect to what happens in the Move Contstructor. Now I get it. – abigagli Apr 06 '14 at 09:18
  • Not concerning loads and stores, but if a class has some memory-allocated resource, it needs to call `delete` both in _destructor_ and _move assignment operator_. Then, `std::swap` results in deleting null pointer 3 times (_unifying assignment operator_ has the very same effect) and compilers might have problems to optimize out these `delete`s (if its even possible), see https://godbolt.org/g/E84ud4. I guess this is a reason why `unique_ptr` provides `swap` specialization. – Daniel Langr Aug 15 '17 at 08:25
4

Is specializing std::swap deprecated now that we have move semantics?

No. This is the generic version, but you can optimize it to skip a third move operation. My preference is to combine copy-and-swap idiom with customizing std::swap for my classes.

That means I will have:

class Aaaa
{
public:
    Aaaa(); // not interesting; defined elsewhere
    Aaaa(Aaaa&& rvalueRef); // same
    Aaaa(const Aaaa& ref); // same
    ~Aaaa(); // same
    Aaaa& operator=(Aaaa object) // copy&swap
    {
        swap(object);
        return *this;
    }
    void swap(Aaaa& other)
    {
        std::swap(dataMember1, other.dataMember1);
        std::swap(dataMember2, other.dataMember2);
        // ...
    }

    // ...
};

namespace std
{
    template<> inline void std::swap(Aaaa& left, Aaaa& right)
    { left.swap(right); }
}
JFMR
  • 23,265
  • 4
  • 52
  • 76
utnapistim
  • 26,809
  • 3
  • 46
  • 82
  • 4
    The correct way to use `swap` is to `using std::swap` followed by an unqualified call to `swap`. Also, specializing `std::swap` is seen as Not So Good™, because you can't partially specialize functions. See [this answer of mine](http://stackoverflow.com/questions/6380862/how-to-provide-a-swap-function-for-my-class/6380882#6380882) for further details. – Xeo Dec 23 '11 at 16:37
  • Are you allowed to add something to namespace std? – Micha Wiedenmann Dec 21 '12 at 12:20
  • 3
    @MichaWiedenmann, you are allowed to add std functions template specializations (as in the example above) – utnapistim Dec 23 '12 at 13:26
0

That will depend on your Types.

You will move it from x to z, from y to x, from z to y. That is three copy operations of the underlying representation (maybe only one pointer, maybe something more, who knows)

Now maybe you can create a faster swap for your type (xor swap trick, inline assembler, or maybe std::swap for your underlying types is just faster).

Or maybe also your compiler is good at optimizing, and essentially optimizes both cases into the same instructions (like have the temporary in a register).

I personally tend to always implement a swap member function that will be called from several places, including things like move assignment, but YMMV.

PlasmaHH
  • 15,673
  • 5
  • 44
  • 57
0

This swap() calls a move constructor and 2 move assignments. I think one can write more efficient swap() for his particular type of class like,

class X
{
    int * ptr_to_huge_array;
public:
// ctors, assgn ops. etc. etc.

    friend void swap(X& a, X& b)
    {
        using std::swap;
        swap(a.ptr_to_huge_array, b.ptr_to_huge_array);
    }
};

regardless of the implementation of move constructor and assignment operator.

ali_bahoo
  • 4,732
  • 6
  • 41
  • 63