3.2.3 std::exchange

What was the problem

PIC <utility>

If you are reading this, you certainly had to write a move constructor at some point. When doing so, the programmer has to “steal” the resources from an instance, and leave the aforementioned instance in a valid state. In the example from section 2.2.1, copied below for convenience, the resource from that is transferred to this, then the field from that is set to nullptr.

struct foo 
{ 
  foo(foo&& that) 
    : m_resource(that.m_resource) 
  { 
    that.m_resource = nullptr; 
  } 
};

As a move constructor grows with the number of fields, the distance between the transfer of the resource in the constructor’s initializer list and the reset in the constructor’s body increases. This blurs the relationship between the two statements, which actually form a single semantic operation. This is also error-prone, as one may easily forget one of the two instructions.

How the Problem is Solved

Thankfully C++14 introduces a nice small utility with std::exchange to solve this issue.

struct foo 
{ 
  foo(foo&& that) 
    // Assign that.m_resource to m_resource and set that.m_resource to 
    // nullptr, in a single statement. 
    : m_resource(std::exchange(that.m_resource, nullptr)) 
  {} 
};

The std::exchange function assigns its second parameter to its first parameter, then returns the previous value of its first parameter. Yes, there is no actual exchange here… As with std::move, the name does not carry the meaning very well, but at least it solves a problem.