Is pass-by-value a reasonable default in C++11?

C++Coding StyleC++11

C++ Problem Overview


In traditional C++, passing by value into functions and methods is slow for large objects, and is generally frowned upon. Instead, C++ programmers tend to pass references around, which is faster, but which introduces all sorts of complicated questions around ownership and especially around memory management (in the event that the object is heap-allocated)

Now, in C++11, we have Rvalue references and move constructors, which mean that it's possible to implement a large object (like an std::vector) that's cheap to pass by value into and out of a function.

So, does this mean that the default should be to pass by value for instances of types such as std::vector and std::string? What about for custom objects? What's the new best practice?

C++ Solutions


Solution 1 - C++

It's a reasonable default if you need to make a copy inside the body. This is what Dave Abrahams is advocating:

> Guideline: Don’t copy your function arguments. Instead, pass them by value and let the compiler do the copying.

In code this means don't do this:

void foo(T const& t)
{
    auto copy = t;
    // ...
}

but do this:

void foo(T t)
{
    // ...
}

which has the advantage that the caller can use foo like so:

T lval;
foo(lval); // copy from lvalue
foo(T {}); // (potential) move from prvalue
foo(std::move(lval)); // (potential) move from xvalue

and only minimal work is done. You'd need two overloads to do the same with references, void foo(T const&); and void foo(T&&);.

With that in mind, I now wrote my valued constructors as such:

class T {
    U u;
    V v;
public:
    T(U u, V v)
        : u(std::move(u))
        , v(std::move(v))
    {}
};

Otherwise, passing by reference to const still is reasonable.

Solution 2 - C++

In almost all cases, your semantics should be either:

bar(foo f); // want to obtain a copy of f
bar(const foo& f); // want to read f
bar(foo& f); // want to modify f

All other signatures should be used only sparingly, and with good justification. The compiler will now pretty much always work these out in the most efficient way. You can just get on with writing your code!

Solution 3 - C++

Pass parameters by value if inside the function body you need a copy of the object or only need to move the object. Pass by const& if you only need non-mutating access to the object.

Object copy example:

void copy_antipattern(T const& t) { // (Don't do this.)
    auto copy = t;
    t.some_mutating_function();
}

void copy_pattern(T t) { // (Do this instead.)
    t.some_mutating_function();
}

Object move example:

std::vector<T> v; 

void move_antipattern(T const& t) {
    v.push_back(t); 
}

void move_pattern(T t) {
    v.push_back(std::move(t)); 
}

Non-mutating access example:

void read_pattern(T const& t) {
    t.some_const_function();
}

For rationale, see these blog posts by Dave Abrahams and Xiang Fan.

Solution 4 - C++

The signature of a function should reflect it's intended use. Readability is important, also for the optimizer.

This is the best precondition for an optimizer to create fastest code - in theory at least and if not in reality then in a few years reality.

Performance considerations are very often overrated in the context of parameter passing. Perfect forwarding is an example. Functions like emplace_back are mostly very short and inlined anyway.

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionDerek ThurnView Question on Stackoverflow
Solution 1 - C++Luc DantonView Answer on Stackoverflow
Solution 2 - C++AyjayView Answer on Stackoverflow
Solution 3 - C++Edward BreyView Answer on Stackoverflow
Solution 4 - C++Patrick FrombergView Answer on Stackoverflow