In GoingNative 2012, there are some discussions on the new coding style for C++11. One interesting thing which is mentioned by Bjarne Stroustrup, Stephan T. Lavavej and Herb Sutter is the most efficient way to pass the argument.

In C++98, pass by reference is in general more efficient because it saves extra copies (if the copy is expensive, otherwise the extra indirection may hurt the perf instead). However, after the introduction of move semantics in C++11, the story is now a little different (Note that premature optimization doesn’t buy you anything, so the following is to give you a full picture of the new thought introduced in C++11, see the guidelines at the end and use it when appropriated)

Let’s see the following example:

#include <iostream>
#include <vector>

using namespace std;

struct String {
    String(const wchar_t *) {
        cout << "ctor" << endl;
    }
    String(const String &) {
        cout << "copy ctor" << endl;
    }
    String(String &&) {
        cout << "move ctor" << endl;
    }
};

void f1(String s) {
    vector<String> v;
    v.push_back(std::move(s));
}
void f2(const String &s) {
    vector<String> v;
    v.push_back(s);
}

void g() {
    String s(L"");
    cout << "f1(s)" << endl;
    f1(s);
    cout << "f1(L"")" << endl;
    f1(L"");
    cout << "f2(s)" << endl;
    f2(s);
    cout << "f2(L"")" << endl;
    f2(L"");
}

If you run the code, you will find that ‘f1’ has one less copy ctor call for rvalue argument and the same  amount of copy ctor call for lvalue argument than ‘f2’, which means ‘f1’ is more efficient (assume that move ctor is much cheaper than copy ctor).

The trick is as follows: for lvalue argument, ‘f1’ has one extra copy to pass the argument because it is by-value, while ‘f2’ has one extra copy to call push_back. So no difference; for rvalue argument, the compiler has to create a temporary ‘String(L“”)’ and pass the temporary to ‘f1’ or ‘f2’ anyway. Because ‘f2’ can take advantage of move ctor when the argument is a temporary (which is an rvalue), the costs to pass the argument are the same now for ‘f1’ and ‘f2’.

This means in C++11 we can get better performance by using pass-by-value approach when:

  1. The parameter type supports move semantics.
  2. The cost of move constructor is much cheaper than the copy constructor (both the time and stack usage).
  3. Inside the function, the parameter type will be passed to another function or operation which supports both copy and move.
  4. It is common to pass a temporary as the argument.

One example for the above guidelines (no copies here!):

// Assume that Matrix has a cheap move ctor
Matrix operator-(Matrix m)
{
    // Manipulate m
    return std::move(m);
}

Matrix m = -(a + b);