Note that there are some explanatory texts on larger screens.

plurals
  1. POMaking swap faster, easier to use and exception-safe
    text
    copied!<p>I could not sleep last night and started thinking about <code>std::swap</code>. Here is the familiar C++98 version:</p> <pre><code>template &lt;typename T&gt; void swap(T&amp; a, T&amp; b) { T c(a); a = b; b = c; } </code></pre> <p>If a user-defined class <code>Foo</code> uses external ressources, this is inefficient. The common idiom is to provide a method <code>void Foo::swap(Foo&amp; other)</code> and a specialization of <code>std::swap&lt;Foo&gt;</code>. Note that this does not work with class <em>templates</em> since you cannot partially specialize a function template, and overloading names in the <code>std</code> namespace is illegal. The solution is to write a template function in one's own namespace and rely on argument dependent lookup to find it. This depends critically on the client to follow the "<code>using std::swap</code> idiom" instead of calling <code>std::swap</code> directly. Very brittle.</p> <p>In C++0x, if <code>Foo</code> has a user-defined move constructor and a move assignment operator, providing a custom <code>swap</code> method and a <code>std::swap&lt;Foo&gt;</code> specialization has little to no performance benefit, because the C++0x version of <code>std::swap</code> uses efficient moves instead of copies:</p> <pre><code>#include &lt;utility&gt; template &lt;typename T&gt; void swap(T&amp; a, T&amp; b) { T c(std::move(a)); a = std::move(b); b = std::move(c); } </code></pre> <p>Not having to fiddle with <code>swap</code> anymore already takes a lot of burden away from the programmer. Current compilers do not generate move constructors and move assignment operators automatically yet, but as far as I know, this will change. The only problem left then is exception-safety, because in general, move operations are allowed to throw, and this opens up a whole can of worms. The question "What exactly is the state of a moved-from object?" complicates things further.</p> <p>Then I was thinking, what exactly are the semantics of <code>std::swap</code> in C++0x if everything goes fine? What is the state of the objects before and after the swap? Typically, swapping via move operations does not touch external resources, only the "flat" object representations themselves.</p> <p>So why not simply write a <code>swap</code> template that does exactly that: <em>swap the object representations</em>?</p> <pre><code>#include &lt;cstring&gt; template &lt;typename T&gt; void swap(T&amp; a, T&amp; b) { unsigned char c[sizeof(T)]; memcpy( c, &amp;a, sizeof(T)); memcpy(&amp;a, &amp;b, sizeof(T)); memcpy(&amp;b, c, sizeof(T)); } </code></pre> <p>This is as efficient as it gets: it simply blasts through raw memory. It does not require any intervention from the user: no special swap methods or move operations have to be defined. This means that it even works in C++98 (which does not have rvalue references, mind you). But even more importantly, <em>we can now forget about the exception-safety issues</em>, because <code>memcpy</code> never throws.</p> <p>I can see two potential problems with this approach:</p> <p>First, not all objects are meant to be swapped. If a class designer hides the copy constructor or the copy assignment operator, trying to swap objects of the class should fail at compile-time. We can simply introduce some dead code that checks whether copying and assignment are legal on the type:</p> <pre><code>template &lt;typename T&gt; void swap(T&amp; a, T&amp; b) { if (false) // dead code, never executed { T c(a); // copy-constructible? a = b; // assignable? } unsigned char c[sizeof(T)]; std::memcpy( c, &amp;a, sizeof(T)); std::memcpy(&amp;a, &amp;b, sizeof(T)); std::memcpy(&amp;b, c, sizeof(T)); } </code></pre> <p>Any decent compiler can trivially get rid of the dead code. (There are probably better ways to check the "swap conformance", but that is not the point. What matters is that it's possible).</p> <p>Second, some types might perform "unusual" actions in the copy constructor and copy assignment operator. For example, they might notify observers of their change. I deem this a minor issue, because such kinds of objects probably should not have provided copy operations in the first place.</p> <p>Please let me know what you think of this approach to swapping. Would it work in practice? Would you use it? Can you identify library types where this would break? Do you see additional problems? Discuss!</p>
 

Querying!

 
Guidance

SQuiL has stopped working due to an internal error.

If you are curious you may find further information in the browser console, which is accessible through the devtools (F12).

Reload