Background

Rvalue reference:

  • An rvalue reference is a reference that will bind only to a temporary object.

std::move, std::forward

  • std::move
    * takes an object and allows you to treat it as a temporary (an rvalue)
    
  • std::forward
    * has a single use case: to cast a templated function parameter (inside the function) to the value category (lvalue or rvalue) the caller used to pass it. 
    * This allows rvalue arguments to be passed on as rvalues, and lvalues to be passed on as lvalues, a scheme called "perfect forwarding."
    

operator =

  • default implementation for operator= is copy
    * it copies each member, it’s calling the constructor that accepting the type on the right of the operator=
    * for example for base_string, the 7th constructor:
            * basic_string( const basic_string& other );
            * Copy constructor. Constructs the string with the copy of the contents of other.
    
  • Thus in order for the the class type to accept rvalue reference, it should also have a constructor accepting a rvalue reference
    * and the constructor should be implemented by std::move
    * see the 8th constructor
    

move constructor

  • Move constructors typically “steal” the resources held by the argument (e.g. pointers to dynamically-allocated objects, file descriptors, TCP sockets, I/O streams, running threads, etc.) rather than make copies of them

Incentive

Move semantics allows you to avoid unnecessary copies when working with temporary objects that are about to evaporate, and whose resources can safely be taken from that temporary object and used by another

If we are creating a temporary objects, and we know that it will only be used for the constructor, specifically it will be passed into the constructor as a class member. Then it’s better for the constructor to directly make the class member pointing to the value of that temporary objects.

class Type{
public Type(string str){
str = str
; // it will definitely be copied
}

public Type(string& str_){
    str = str_; // it will also be copied, see the reference constructor (7th constructor) of base_string
}

public Type(string&& str_){
    str = std::move(str_); // first, std::move will ensure that str_ is accepted as a rvalue. Then this will call the rvalue constructor (also called move constructor) of base_string
}
    public string& str;

}

But if we are passing in the parameter as an object or reference.

  • in object’s case, we know that it will be copied
  • in reference’s case, we also know that it’s a copy constructor (explained above in the operator=)

thus in either case, it will be copied, this is not good for performance.

With rvalue reference as a parameter, and std::move/forward, we can let the class member directly pointing to the temporary object.

class Type{

public Type(string&& str_){
    // first, std::move will ensure that str_ is accepted as a rvalue. 
    // Then this will call the rvalue constructor (also called move constructor) of base_string
    str = std::move(str_);
}
    public string& str;

}

Case Study

Case 1

In Mesos project, we have this usage:

template
inline mesos::v1::Resource::DiskInfo createDiskInfo(Args&&… args)
{
return common::createDiskInfo(
std::forward(args)…);
}

It’s accepting Args, the parameter pack as a rvalue reference. The reason is that: our usage for these functions looks like this

Resource volume1 = Resources::parse(“disk”, “128”, “role1”).get();
volume1.mutable_disk()->CopyFrom(createDiskInfo(“id1”, “path1”));

where the parameters are strings and they are created specifically for the construction of the DiskInfo, thus in order to avoid copying the value, we accept rvalue reference for the parameter of the DiskInfo constructor, then it will call the move constructor of base_string, thus copies are avoided

Case 2

In the case below, the authorizer.get()->getObjectApprover return a Owned, and this is only used to construct the AuthorizationAcceptor. In the use of Owned, we don’t want to use to copy the value inside, since T is a shared pointer. To avoid that, we can either use Owned’s swap method, which swap the value inside the object. Or we can use rvalue reference to directly make the class member pointing to the original value.

Future> AuthorizationAcceptor::create(
const Option& principal,
const Option& authorizer,
const authorization::Action& action)
{
if (authorizer.isNone()) {
return Owned(
new AuthorizationAcceptor(Owned(
new AcceptingObjectApprover())));
}

const Option subject =
authorization::createSubject(principal);

return authorizer.get()->getObjectApprover(subject, action)
.then(= {
return Owned(
new AuthorizationAcceptor(approver));
});
}

References