Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

c++ rvalue reference and move semantics

So I was watching c++ videos on youtube yesterday and came across one the was about C++-11 rvalue reference and move semantics. I think I understand the concept in broad terms, but today when I was going through my code with the TA he asked why i did not us a reference (like std::pair<HostName, IPAddress>& p) in the code below. I had not thought about it at all in this case, but when he asked I remembered the video saying "In C++-11 you should generally use pass by value."

My question is thus: In the code below, would std::pair<HostName, IPAddress> p be better off like std::pair<HostName, IPAddress>& p or not? Will move semantics be used and would it make a difference?

IPAddress NameServer::lookup( const HostName& host ) const {
    auto it = std::find_if( vec.begin(), vec.end(),
                       [host] ( std::pair<HostName, IPAddress> p ) {
        return p.first == host;
    } );
    ...
}
like image 375
evading Avatar asked Feb 28 '13 15:02

evading


1 Answers

In this case, you should pass by const reference. Passing by value makes sense when you eventually want to generate a copy, or a move, of the passed value; if you do not want to copy nor to move, and especially if you want to observe only, you should pass by (const) reference.

Here, your lambda predicate does not need to generate any copy of the pair it receives in input: therefore, there is no reason to pass by value (nor to capture by vaue).

IPAddress NameServer::lookup( const HostName& host ) const {
    auto it = std::find_if( vec.begin(), vec.end(),
        [&host] ( std::pair<HostName, IPAddress> const& p ) {
    //   ^^^^^                                   ^^^^^^
        return p.first == host;
    } );
    ...
}

Consider, instead, this case (typical C++03 code):

struct A
{
    A(string const& s) : _s(s) { }
private:
    string _s;
};

In C++11, since you have move semantics, rather than passing s by constant reference you could simply pass by value and move it into the member variable:

struct A
{
    A(string s) : _s(move(s)) { }
private:
    string _s;
};

This makes sense because we always end up generating a copy of the value being passed.

As correctly pointed out by Benjamin Lindley in the comments, if this is acceptable to you, you could write overloads of the above constructor that take their argument by reference:

struct A
{
    A(string const& s) : _s(s) { }    // 1 copy
    A(string&& s) : _s(move(s)) { }   // 1 move
private:
    string _s;
};

The above version allows performing just one copy for lvalues, and one move for rvalues, whereas the version that passes by value always performs one additional move. Therefore, this solution may be preferable if moving is an expensive operation for your argument types (this is not the case for string, but could be the case for other types).

However, doing so may be cumbersome if your function takes several arguments. To reduce the effort, you could then write a single function template that takes universal references and perfect-forwards its arguments. This Q&A on StackOverflow is relevant to the subject.

like image 82
Andy Prowl Avatar answered Oct 19 '22 12:10

Andy Prowl