71

How is the swap function implemented in the STL? Is it as simple as this:

template<typename T> void swap(T& t1, T& t2) {
    T tmp(t1);
    t1=t2;
    t2=tmp;
}

In other posts, they talk about specializing this function for your own class. Why would I need to do this? Why can't I use the std::swap function?

Niall
  • 28,102
  • 9
  • 90
  • 124
Maximilian Mordig
  • 1,096
  • 1
  • 9
  • 12
  • 26
    That's exactly how `std::swap` was implemented in C++03, in C++11 it is `T tmp(std::move(t1)); t1 = std::move(t2); t2 = std::move(tmp);` as that will be more efficient than copying for some types. – Jonathan Wakely Aug 13 '14 at 12:54
  • 5
    There is no "the standard library". There are a [bunch of implementations](https://en.wikipedia.org/wiki/Standard_Template_Library#Implementations), pick one or a few and look it up. – nwp Aug 13 '14 at 13:00
  • 9
    @nwp: Each of those implementations implements "The C++ standard Library" see §17.1/1 – Mooing Duck Aug 13 '14 at 16:39
  • There is, however, no "the STL". The Standard Library is not the STL. – Mr Lister Aug 13 '14 at 20:06
  • 5
    @MrLister: There's totally "the STL". It's a C++ library written by SGI, which has striking similarity to the C++ standard library. https://www.sgi.com/tech/stl/table_of_contents.html – Mooing Duck Aug 13 '14 at 20:12

2 Answers2

72

How is std::swap implemented?

Yes, the implementation presented in the question is the classic C++03 one.

A more modern (C++11) implementation of std::swap looks like this:

template<typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1); // or T temp(std::move(t1));
    t1 = std::move(t2);
    t2 = std::move(temp);
}

This is an improvement over the classic C++03 implementation in terms of resource management because it prevents unneeded copies, etc. It, the C++11 std::swap, requires the type T to be MoveConstructible and MoveAssignable, thus allowing for the implementation and the improvements.

Why would I need to provide a custom implementation?

A custom implementation of swap, for a specific type, is usually advised when your implementation is more efficient or specific than the standard version.

A classic (pre-C++11) example of this is when your class manages a large amount of resources that would be expensive to copy and then delete. Instead, your custom implementation could simply exchange the handles or pointers required to effect the swap.

With the advent of std::move and movable types (and implemented your type as such), circa C++11 and onwards, a lot of the original rationale here is starting to fall away; but nevertheless, if a custom swap would be better than the standard one, implement it.

Generic code will generally be able to use your custom swap if it uses the ADL mechanism appropriately.

Community
  • 1
  • 1
Niall
  • 28,102
  • 9
  • 90
  • 124
  • Can you give an example where it would still make sense to specialize swap if the class implements the move-constructor? – Maximilian Mordig Aug 13 '14 at 13:08
  • @UnnamedOne, if the move constructors are implemented, then I can't think of a reasonable example off-hand. Basically, the updated `swap` is designed to do just that, minimised the requirements on you to extend or customise the standard implementations (which is a good thing in my opinion). – Niall Aug 13 '14 at 13:14
  • 2
    One possible reason would be that if setting up the state of a moved-from object isn't *completely* trivial, and isn't optimized away in the standard `swap` implementation, then you could write a more efficient swap than three moves by not bothering with that setup. That's a lot of "ifs" of course, but suppose a moved-from object is defined to be "empty" and an "empty" object contains a whole bunch of zeroes and maybe a dummy node structure. It's a few writes, times three. So normally negligible, but you might do it for performance if it arises. – Steve Jessop Aug 13 '14 at 14:38
  • 2
    ... alternatively consider something like a string with a short string optimization. You'd implement the move constructor of course (to transfer the dynamic buffers of long strings), but you might *also* implement swap in order to (try to) swap the contents of the short-string buffers more efficiently than three copies of the buffer contents to three different memory regions. – Steve Jessop Aug 13 '14 at 14:41
  • @SteveJessop that means you should rethink your moved-from state (which is meant to be as simple and least resource expensive as possible) – ratchet freak Aug 13 '14 at 15:13
  • 2
    @ratchetfreak: I'm talking about the case where "least resource expensive possible" is nevertheless more expensive than "zero cost". If you mean that a moved-from state should be zero cost to establish then OK, but although clearing a pointer field is effectively zero cost in almost all circumstances, it isn't strictly so. – Steve Jessop Aug 13 '14 at 16:05
  • 5
    A `swap` member function can temporarily break invariants if doing so is faster than doing three full moves that have to maintain invariants after each move. If there's an efficient `X::swap(X&)` member then specializing `swap(X&, X&)` to call it makes sense. Some move operations might even be implemented in terms of the `swap` member, so the generic `std::swap` function would call the `swap` member three times, when it only needs to be done once. – Jonathan Wakely Aug 13 '14 at 17:07
  • Where can I find source code for the standard to validate it is so? – Ako Mar 15 '19 at 17:51
  • @Ako, your standard libraries code, easiest may to use your code editor to browse to it. Remember each one may be different, the library is free to implement as needed so long as the pre and post conditions etc are met. – Niall Mar 17 '19 at 13:44
19

How is the swap function implemented in the STL?

Which implementation? It's a specification, not a single concrete library. If you mean how does my compiler's standard library do it, either tell us which compiler that is, or read the code yourself.

Is it as simple as this:

That's essentially the naive version pre-C++11.

This un-specialized implementation forces a copy: for T = std::vector<SomethingExpensive> in your example, the code translates as:

template<typename T> void swap(T& t1, T& t2) {
  T tmp(t1); // duplicate t1, making an expensive copy of each element
  t1=t2;     // discard the original contents of t1,
             // and replace them with an expensive duplicate of t2
  t2=tmp;    // discard the original contents of t2,
             // and replace them with an expensive duplicate of tmp
}            // implicitly destroy the expensive temporary copy of t1

so to exchange two vectors we essentially created three. There were three dynamic allocations and a lot of expensive objects copied, and any of those operations could throw, possibly leaving the arguments in an indeterminate state.

Since this was obviously awful, overloads were provided for expensive containers, and you were encouraged to write overloads for your own expensive types: eg. the std::vector specialization had access to the vector's internals, and could swap two vectors without all the copying:

template <typename T> void swap(vector<T> &v1, vector<T> &v2) { v1.swap(v2); }
template <typename T> void vector<T>::swap(vector<T>& other) {
  swap(this->size_, other.size_); // cheap integer swap of allocated count
  swap(this->used_, other.used_); // cheap integer swap of used count
  swap(this->data__, other.data_); // cheap pointer swap of data ptr
}

Note that this involves no copies at all of anything expensive, no dynamic (de)allocation, and is guaranteed not to throw.

Now, the reason for this specialization is that vector::swap has access to vector's internals, and can safely and efficiently move them around without copying.

Why would I need to do this [specializing ... for your own class] ?

Pre-C++11, for the same reason as std::vector - to make swapping efficient and exception-safe.

Since C++11, you really don't - if you either provide move construction and assignment, or the compiler can generate sane defaults for you.

The new generic swap:

template <typename T> void swap(T& t1, T& t2) {
    T temp = std::move(t1);
    t1 = std::move(t2);
    t2 = std::move(temp);
}

can use move construction/assignment to get essentially the same behaviour as the custom vector implementation above, without needing to write a custom implementation at all.

Useless
  • 55,472
  • 5
  • 73
  • 117
  • I use Visual Studio 2005 c++ (doesn't support c++11, but ignore). How can I access the source code? I couldn't find anything; it might be proprietary. – Maximilian Mordig Aug 13 '14 at 13:23
  • 1
    The header files need to be somewhere the compiler can use them, but I don't know the path. – Useless Aug 13 '14 at 13:27
  • 2
    @UnnamedOne In Visual Studio you can probably right click on `swap` and choose "Go To Definition". – nwp Aug 13 '14 at 15:16
  • @UnnamedOne there is a shortcut to go to definition, otherwise look in the install directory (in program files) and look for the folder called "include" – ratchet freak Aug 13 '14 at 15:16
  • @UnnamedOne I think the default directory for 2005 is "C:\Program Files (x86)\Microsoft Visual Studio 8.0\VC\include\" – Mooing Duck Aug 13 '14 at 20:16