I saw code somewhere in which someone decided to copy an object and subsequently move it to a data member of a class. This left me in confusion in that I thought the whole point of moving was to avoid copying. Here is the example:
struct S
{
S(std::string str) : data(std::move(str))
{}
};
Here are my questions:
Why aren't we taking an rvalue-reference to str?
Won't a copy be expensive, especially given something like std::string?
What would be the reason for the author to decide to make a copy then a move?
When should I do this myself?
Before I answer your questions, one thing you seem to be getting wrong: taking by value in C++11 does not always mean copying. If an rvalue is passed, that will be moved (provided a viable move constructor exists) rather than being copied. And std::string does have a move constructor.
Unlike in C++03, in C++11 it is often idiomatic to take parameters by value, for the reasons I am going to explain below. Also see this Q&A on StackOverflow for a more general set of guidelines on how to accept parameters.
Why aren't we taking an rvalue-reference to str?
Because that would make it impossible to pass lvalues, such as in:
std::string s = "Hello";
S obj(s); // s is an lvalue, this won't compile!
If S only had a constructor that accepts rvalues, the above would not compile.
Won't a copy be expensive, especially given something like std::string?
If you pass an rvalue, that will be moved into str, and that will eventually be moved into data. No copying will be performed. If you pass an lvalue, on the other hand, that lvalue will be copied into str, and then moved into data.
So to sum it up, two moves for rvalues, one copy and one move for lvalues.
What would be the reason for the author to decide to make a copy then a move?
First of all, as I mentioned above, the first one is not always a copy; and this said, the answer is: "Because it is efficient (moves of std::string objects are cheap) and simple".
Under the assumption that moves are cheap (ignoring SSO here), they can be practically disregarded when considering the overall efficiency of this design. If we do so, we have one copy for lvalues (as we would have if we accepted an lvalue reference to const) and no copies for rvalues (while we would still have a copy if we accepted an lvalue reference to const).
This means that taking by value is as good as taking by lvalue reference to const when lvalues are provided, and better when rvalues are provided.
P.S.: To provide some context, I believe this is the Q&A the OP is referring to.
To understand why this is a good pattern, we should examine the alternatives, both in C++03 and in C++11.
We have the C++03 method of taking a std::string const&:
struct S
{
std::string data;
S(std::string const& str) : data(str)
{}
};
in this case, there will always be a single copy performed. If you construct from a raw C string, a std::string will be constructed, then copied again: two allocations.
There is the C++03 method of taking a reference to a std::string, then swapping it into a local std::string:
struct S
{
std::string data;
S(std::string& str)
{
std::swap(data, str);
}
};
that is the C++03 version of "move semantics", and swap can often be optimized to be very cheap to do (much like a move). It also should be analyzed in context:
S tmp("foo"); // illegal
std::string s("foo");
S tmp2(s); // legal
and forces you to form a non-temporary std::string, then discard it. (A temporary std::string cannot bind to a non-const reference). Only one allocation is done, however. The C++11 version would take a && and require you to call it with std::move, or with a temporary: this requires that the caller explicitly creates a copy outside of the call, and move that copy into the function or constructor.
struct S
{
std::string data;
S(std::string&& str): data(std::move(str))
{}
};
Use:
S tmp("foo"); // legal
std::string s("foo");
S tmp2(std::move(s)); // legal
Next, we can do the full C++11 version, that supports both copy and move:
struct S
{
std::string data;
S(std::string const& str) : data(str) {} // lvalue const, copy
S(std::string && str) : data(std::move(str)) {} // rvalue, move
};
We can then examine how this is used:
S tmp( "foo" ); // a temporary `std::string` is created, then moved into tmp.data
std::string bar("bar"); // bar is created
S tmp2( bar ); // bar is copied into tmp.data
std::string bar2("bar2"); // bar2 is created
S tmp3( std::move(bar2) ); // bar2 is moved into tmp.data
It is pretty clear that this 2 overload technique is at least as efficient, if not more so, than the above two C++03 styles. I'll dub this 2-overload version the "most optimal" version.
Now, we'll examine the take-by-copy version:
struct S2 {
std::string data;
S2( std::string arg ):data(std::move(x)) {}
};
in each of those scenarios:
S2 tmp( "foo" ); // a temporary `std::string` is created, moved into arg, then moved into S2::data
std::string bar("bar"); // bar is created
S2 tmp2( bar ); // bar is copied into arg, then moved into S2::data
std::string bar2("bar2"); // bar2 is created
S2 tmp3( std::move(bar2) ); // bar2 is moved into arg, then moved into S2::data
If you compare this side-by-side with the "most optimal" version, we do exactly one additional move! Not once do we do an extra copy.
So if we assume that move is cheap, this version gets us nearly the same performance as the most-optimal version, but 2 times less code.
And if you are taking say 2 to 10 arguments, the reduction in code is exponential -- 2x times less with 1 argument, 4x with 2, 8x with 3, 16x with 4, 1024x with 10 arguments.
Now, we can get around this via perfect forwarding and SFINAE, allowing you to write a single constructor or function template that takes 10 arguments, does SFINAE to ensure that the arguments are of appropriate types, and then moves-or-copies them into the local state as required. While this prevents the thousand fold increase in program size problem, there can still be a whole pile of functions generated from this template. (template function instantiations generate functions)
And lots of generated functions means larger executable code size, which can itself reduce performance.
For the cost of a few moves, we get shorter code and nearly the same performance, and often easier to understand code.
Now, this only works because we know, when the function (in this case, a constructor) is called, that we will be wanting a local copy of that argument. The idea is that if we know that we are going to be making a copy, we should let the caller know that we are making a copy by putting it in our argument list. They can then optimize around the fact that they are going to give us a copy (by moving into our argument, for example).
Another advantage of the 'take by value" technique is that often move constructors are noexcept. That means the functions that take by-value and move out of their argument can often be noexcept, moving any throws out of their body and into the calling scope (who can avoid it via direct construction sometimes, or construct the items and move into the argument, to control where throwing happens). Making methods nothrow is often worth it.
This is probably intentional and is similar to the copy and swap idiom. Basically since the string is copied before the constructor, the constructor itself is exception safe as it only swaps (moves) the temporary string str.
You don't want to repeat yourself by writing a constructor for the move and one for the copy:
S(std::string&& str) : data(std::move(str)) {}
S(const std::string& str) : data(str) {}
This is much boilerplate code, especially if you have multiple arguments. Your solution avoids that duplication on the cost of an unnecessary move. (The move operation should be quite cheap, however.)
The competing idiom is to use perfect forwarding:
template <typename T>
S(T&& str) : data(std::forward<T>(str)) {}
The template magic will choose to move or copy depending on the parameter that you pass in. It basically expands to the first version, where both constructor were written by hand. For background information, see Scott Meyer's post on universal references.
From a performance aspect, the perfect forwarding version is superior to your version as it avoids the unnecessary moves. However, one can argue that your version is easier to read and write. The possible performance impact should not matter in most situations, anyway, so it seems to be a matter of style in the end.
Related
I think my understanding of rvalue references and move semantics has some holes in it.
As far as I've rvalue references understood now, I could implement a function f in two ways such that it profits from move semantics.
The first version: implement both
void f(const T& t);
void f(T&& t);
This would result in quite some redundancy, as both versions are likely to have (almost) identical implementation.
second version: implement only
void f(T t);
Calling f would result in calling either the copy or the move constructor of T.
Question.
How do the two versions compare to each other? My suspicion:
In the second version, (ownership of) dynamically allocated data may be moved by the move constructor, while non-dynamically allocated data in t and its members needs to be copied. In the first version, neither version allocates any additional memory (except the pointer behind the reference).
If this is correct, can I avoid writing the implementation of f basically twice without the drawback of the second version?
If you need to take a T&& parameter, it usually means you want to move the object somewhere and keep it. This kind of function is typically paired up with a const T& overload so it can accept both rvalues and lvalues.
In this situation, the second version (only one overload with T as a parameter) is always less efficient, but most likely not by much.
With the first version, if you pass an lvalue, the function takes a reference and then makes a copy to store it somewhere. That's one copy construction or assignment. If you pass an rvalue, the function again takes a reference and then moves the object to store it. That's one move construction or assignment.
With the second version, if you pass an lvalue, it gets copied into the parameter, and then the function can move that. If you pass an rvalue, if gets moved (assuming the type has a move constructor) into the parameter, and then the function can also move that. In both cases, that's one more move construction or assignment than with the first version.
Also note that copy elision can happen in some cases.
The benefit of the second version is that you don't need multiple overloads. With the first version, you need 2^n overloads for n parameters that you need copied or moved.
In the simple case of just one parameter, I sometimes do something like this to avoid repeating code:
void f(const T& t) {
f_impl(t);
}
void f(T&& t) {
f_impl(std::move(t));
}
// this function is private or just inaccessible to the user
template<typename U>
void f_impl(U&& t) {
// use std::forward<U>(t)
}
Let's say I have a buffer of chars in memory that holds a c_string, and I want to add an object of std::string with the content of that c_string to a standard container, such as std::list<std::string>, in an efficient way.
Example:
#include <list>
#include <string>
int main()
{
std::list<std::string> list;
char c_string_buffer[] {"For example"};
list.push_back(c_string_buffer); // 1
list.emplace_back(c_string_buffer); // 2
list.push_back(std::move(std::string(c_string_buffer))); // 3
}
I use ReSharper C++, and it complains about #1 (and suggests #2).
When I read push_back vs emplace_back, it says that when it is not an rvalue, the container will store a "copied" copy, not a moved copy. Meaning, #2 does the same as #1. Don’t blindly prefer emplace_back to push_back also talks about that.
Case 3: When I read What's wrong with moving?, it says that what std::move() does "is a cast to a rvalue reference, which may enable moving under some conditions".
--
Does #3 actually give any benefit? I assume that the constructor of std::string is called and creates a std::string object with the content of the c_string. I am not sure if later the container constructs another std::string and copies the 1st object to the 2nd object.
// 3 is fully equivalent to // 1. std::move does absolutely nothing here since std::string(c_string_buffer) is already a rvalue.
The problem with push_back is not related to move vs copy.
push_back is always a bad choice if you don't yet have an object of the element type because it always creates the new container element via copy or move construction from another object of the element type.
If you write list.push_back(c_string_buffer); // 1, then because push_back expects a std::string&& argument (or const std::string&), a temporary object of type std::string will be constructed from c_string_buffer and passed-by-reference to push_back. push_back then constructs the new element from this temporary.
With // 3 you are just making the temporary construction that would otherwise happen implicitly explicit.
The second step above can be avoided completely by emplace_back. Instead of taking a reference to an object of the target type, it takes arbitrary arguments by-reference and then constructs the new element directly from the arguments. No temporary std::string is needed.
Does 3 actually give any benefit?
No. #1 already does a move without std::move. #3 is just an unnecessarily explicit way to write #1.
#2 is generally potentially most efficient. That's why your static analyzer suggests it. But, as the article explains, it's not significant in this case, and has a potential compile-time penalty. I believe you can avoid the potential compile-time cost by using list.emplace_back(+c_string_buffer);, but that may be confusing to the reader.
Since we have move semantics in C++, nowadays it is usual to do
void set_a(A a) { _a = std::move(a); }
The reasoning is that if a is an rvalue, the copy will be elided and there will be just one move.
But what happens if a is an lvalue? It seems there will be a copy construction and then a move assignment (assuming A has a proper move assignment operator). Move assignments can be costly if the object has too many member variables.
On the other hand, if we do
void set_a(const A& a) { _a = a; }
There will be just one copy assignment. Can we say this way is preferred over the pass-by-value idiom if we will pass lvalues?
Expensive-to-move types are rare in modern C++ usage. If you are concerned about the cost of the move, write both overloads:
void set_a(const A& a) { _a = a; }
void set_a(A&& a) { _a = std::move(a); }
or a perfect-forwarding setter:
template <typename T>
void set_a(T&& a) { _a = std::forward<T>(a); }
that will accept lvalues, rvalues, and anything else implicitly convertible to decltype(_a) without requiring extra copies or moves.
Despite requiring an extra move when setting from an lvalue, the idiom is not bad since (a) the vast majority of types provide constant-time moves and (b) copy-and-swap provides exception safety and near-optimal performance in a single line of code.
But what happens if a is an lvalue? It seems there will be a copy
construction and then a move assignment (assuming A has a proper move
assignment operator). Move assignments can be costly if the object has
too many member variables.
Problem well spotted. I wouldn't go as far as to say that the pass-by-value-and-then-move construct is a bad idiom but it definitely has its potential pitfalls.
If your type is expensive to move and / or moving it is essentially just a copy, then the pass-by-value approach is suboptimal. Examples of such types would include types with a fixed size array as a member: It may be relatively expensive to move and a move is just a copy. See also
Small String Optimization and Move Operations and
"Want speed? Measure." (by Howard Hinnant)
in this context.
The pass-by-value approach has the advantage that you only need to maintain one function but you pay for this with performance. It depends on your application whether this maintenance advantage outweighs the loss in performance.
The pass by lvalue and rvalue reference approach can lead to maintenance headaches quickly if you have multiple arguments. Consider this:
#include <vector>
using namespace std;
struct A { vector<int> v; };
struct B { vector<int> v; };
struct C {
A a;
B b;
C(const A& a, const B& b) : a(a), b(b) { }
C(const A& a, B&& b) : a(a), b(move(b)) { }
C( A&& a, const B& b) : a(move(a)), b(b) { }
C( A&& a, B&& b) : a(move(a)), b(move(b)) { }
};
If you have multiple arguments, you will have a permutation problem. In this very simple example, it is probably still not that bad to maintain these 4 constructors. However, already in this simple case, I would seriously consider using the pass-by-value approach with a single function
C(A a, B b) : a(move(a)), b(move(b)) { }
instead of the above 4 constructors.
So long story short, neither approach is without drawbacks. Make your decisions based on actual profiling information, instead of optimizing prematurely.
The current answers are quite incomplete. Instead, I will try to conclude based on the lists of pros and cons I find.
Short answer
In short, it may be OK, but sometimes bad.
This idiom, namely the unifying interface, has better clarity (both in conceptual design and implementation) compared to forwarding templates or different overloads. It is sometimes used with copy-and-swap (actually, as well as move-and-swap in this case).
Detailed analysis
The pros are:
It needs only one function for each parameter list.
It needs indeed only one, not multiple ordinary overloads (or even 2n overloads when you have n parameters when each one can be unqualified or const-qualified).
Like within a forwarding template, parameters passed by value are compatible with not only const, but volatile, which reduce even more ordinary overloads.
Combined with the bullet above, you don't need 4n overloads to serve to {unqulified, const, const, const volatile} combinations for n parameters.
Compared to a forwarding template, it can be a non-templated function as long as the parameters are not needed to be generic (parameterized through template type parameters). This allows out-of-line definitions instead of template definitions needed to be instantiated for each instance in each translation unit, which can make significant improvement to translation-time performance (typically, during both compiling and linking).
It also makes other overloads (if any) easier to implement.
If you have a forwarding template for a parameter object type T, it may still clash with overloads having a parameter const T& in the same position, because the argument can be a lvalue of type T and the template instantiated with type T& (rather than const T&) for it can be more preferred by the overloading rule when there is no other way to differentiate which is the best overloading candidate. This inconsistency may be quite surprising.
In particular, consider you have forwarding template constructor with one parameter of type P&& in a class C. How many time will you forget to excluded the instance of P&& away from possibly cv-qualified C by SFINAE (e.g. by adding typename = enable_if_t<!is_same<C, decay_t<P>> to the template-parameter-list), to ensure it does not clash with copy/move constructors (even when the latter are explicitly user-provided)?
Since the parameter is passed by value of a non-reference type, it can force the argument be passed as a prvalue. This can make a difference when the argument is of a class literal type. Consider there is such a class with a static constexpr data member declared in some class without an out-of-class definition, when it is used as an argument to a parameter of lvalue reference type, it may eventually fail to link, because it is odr-used and there is no definition of it.
Note since ISO C++ 17 the rules of static constexpr data member have changed to introduce a definition implicitly, so the difference is not significant in this case.
The cons are:
A unifying interface can not replace copy and move constructors where the parameter object type is identical to the class. Otherwise, copy-initialization of the parameter would be infinite recursion, because it will call the unifying constructor, and the constructor then call itself.
As mentioned by other answers, if the cost of copy is not ignorable (cheap and predictable enough), this means you will almost always have the degeneration of performance in the calls when the copy is not needed, because copy-initialization of a unifying passed-by-value parameter unconditionally introduce a copy (either copied-to or moved-to) of the argument unless elided.
Even with mandatory elision since C++17, copy-initialization of a parameter object is still hardly free to be removed away - unless the implementation try very hard to prove the behavior not changed according to as-if rules instead of the dedicated copy elision rules applicable here, which might be sometimes impossible without a whole program analysis.
Likewise, the cost of destruction may not be ignorable as well, particularly when non-trivial subobjects are taken into account (e.g. in cases of containers). The difference is that, it does not only apply to the copy-initialization introduced by the copy construction, but also by the move construction. Making move cheaper than copy in constructors can not improve the situation. The more cost of copy-initialization, the more cost of destruction you have to afford.
A minor shortcoming is that there is no way to tweak the interface in different ways as plural overloads, for example, specifying different noexcept-specifiers for parameters of const& and && qualified types.
OTOH, in this example, unifying interface will usually provide you with noexcept(false) copy + noexcept move if you specifies noexcept, or always noexcept(false) when you specify nothing (or explicit noexcept(false)). (Note in the former case, noexcept does not prevent throwing during copy because that will only occur during evaluation of arguments, which is out of the function body.) There is no further chance to tune them separately.
This is considered minor because it is not frequently needed in reality.
Even if such overloads are used, they are probably confusing by nature: different specifiers may hide subtle but important behavioral differences which are difficult to reason about. Why not different names instead of overloads?
Note the example of noexcept may be particularly problematic since C++17 because noexcept-specification now affect the function type. (Some unexpected compatibility issues can be diagnosed by Clang++ warning.)
Sometimes the unconditional copy is actually useful. Because composition of operations with strong-exception guarantee does not hold the guarantee in nature, a copy can be used as a transactional state holder when the strong-exception guarantee is required and the operation cannot be broken down as sequence of operations with no less strict (no-exception or strong) exception guarantee. (This includes the copy-and-swap idiom, although assignments are not recommended to be unified for other reasons in general, see below.) However, this does not mean the copy is otherwise unacceptable. If the intention of the interface is always to create some object of type T, and the cost of moving T is ignorable, the copy can be moved to the target without unwanted overhead.
Conclusions
So for some given operations, here are suggestions about whether using a unifying interface to replace them:
If not all of the parameter types match the unifying interface, or if there is behavioral difference other than the cost of new copies among operations being unified, there cannot be a unifying interface.
If the following conditions are failed to be fit for all parameters, there cannot be a unifying interface. (But it can still be broken down to different named-functions, delegating one call to another.)
For any parameter of type T, if a copy of each argument is needed for all operations, use unifying.
If both copy and move construction of T have ignorable cost, use unifying.
If the intention of the interface is always to create some object of type T, and the cost of the move construction of T is ignorable, use unifying.
Otherwise, avoid unifying.
Here are some examples need to avoid unifying:
Assignment operations (including assignment to the subobjects thereof, typically with copy-and-swap idiom) for T without ignorable cost in copy and move constructions does not meet the criteria of unifying, because the intention of assignment is not to create (but to replace the content of) the object. The copied object will eventually be destructed, which incurs unnecessary overhead. This is even more obvious for cases of self-assignment.
Insertion of values to a container does not meet the criteria, unless both the copy-initialization and destruction have ignorable cost. If the operation fails (due to the allocation failure, duplicate values or so on) after copy-initialization, the parameters have to be destructed, which incurs unnecessary overhead.
Conditionally creation of object based on parameters will incur the overhead when it does not actually create the object (e.g. std::map::insert_or_assign-like container insertion even in spite of the failure above).
Note the accurate limit of "ignorable" cost is somewhat subjective because it eventually depends on how much cost can be tolerated by the developers and/or the users, and it may vary case by case.
Practically, I (conservatively) assume any trivially copyable and trivailly destructible type whose size is not more than one machine word (like a pointer) qualifying the criteria of ignorable cost in general - if the resulted code actually cost too much in such case, it suggests either a wrong configuration of the build tool is used, or the toolchain is not ready for production.
Do profile if there is any further doubt on performance.
Additional case study
There are some other well-known types preferred to be passed by value or not, depending on the conventions:
Types need to preserve reference values by convention should not be passed by value.
A canonical example is the argument forwarding call wrapper defined in ISO C++, which requires to forward references. Note in the caller position it may also preserve the reference respecting to the ref-qualifier.
An instance of this example is std::bind. See also the resolution of LWG 817.
Some generic code may directly copy some parameters. It may be even without std::move, because the cost of the copy is assumed to be ignorable and a move does not necessarily make it better.
Such parameters include iterators and function objects (except the case of argument forwarding caller wrappers discussed above).
Note the constructor template of std::function (but not the assignment operator template) also uses the pass-by-value functor parameter.
Types presumably having the cost comparable to pass-by-value parameter types with ignorable cost are also preferred to be pass-by-value. (Sometimes they are used as dedicated alternatives.) For example, instances of std::initializer_list and std::basic_string_view are more or less two pointers or a pointer plus a size. This fact makes them cheap enough to be directly passed without using references.
Some types should be better avoided passed by value unless you do need a copy. There are different reasons.
Avoid copy by default, because the copy may be quite expensive, or at least it is not easy to guarantee the copy is cheap without some inspection of the runtime properties of the value being copied. Containers are typical examples in this sort.
Without statically knowing how many elements in a container, it is generally not safe (in the sense of a DoS attack, for example) to be copied.
A nested container (of other containers) will easily make the performance problem of copying worse.
Even empty containers are not guaranteed cheap to be copied. (Strictly speaking, this depends on the concrete implementation of the container, e.g. the existence of the "sentinel" element for some node-based containers... But no, keep it simple, just avoid copying by default.)
Avoid copy by default, even when the performance is totally uninterested, because there can be some unexpected side effects.
In particular, allocator-awared containers and some other types with similar treatment to allocators ("container semantics", in David Krauss' word), should not be passed by value - allocator propagation is just another big semantic worm can.
A few other types conventionally depend. For example, see GotW #91 for shared_ptr instances. (However, not all smart pointers are like that; observer_ptr are more like raw pointers.)
For the general case where the value will be stored, the pass-by-value only is a good compromise-
For the case where you know that only lvalues will be passed (some tightly coupled code) it's unreasonable, unsmart.
For the case where one suspects a speed improvement by providing both, first THINK TWICE, and if that didn't help, MEASURE.
Where the value will not be stored I prefer the pass by reference, because that prevents umpteen needless copy operations.
Finally, if programming could be reduced to unthinking application of rules, we could leave it to robots. So IMHO it's not a good idea to focus so much on rules. Better to focus on what the advantages and costs are, for different situations. Costs include not only speed, but also e.g. code size and clarity. Rules can't generally handle such conflicts of interest.
Pass by value, then move is actually a good idiom for objects that you know are movable.
As you mentioned, if an rvalue is passed, it'll either elide the copy, or be moved, then within the constructor it will be moved.
You could overload the copy constructor and move constructor explicitly, however it gets more complicated if you have more than one parameter.
Consider the example,
class Obj {
public:
Obj(std::vector<int> x, std::vector<int> y)
: X(std::move(x)), Y(std::move(y)) {}
private:
/* Our internal data. */
std::vector<int> X, Y;
}; // Obj
Suppose if you wanted to provide explicit versions, you end up with 4 constructors like so:
class Obj {
public:
Obj(std::vector<int> &&x, std::vector<int> &&y)
: X(std::move(x)), Y(std::move(y)) {}
Obj(std::vector<int> &&x, const std::vector<int> &y)
: X(std::move(x)), Y(y) {}
Obj(const std::vector<int> &x, std::vector<int> &&y)
: X(x), Y(std::move(y)) {}
Obj(const std::vector<int> &x, const std::vector<int> &y)
: X(x), Y(y) {}
private:
/* Our internal data. */
std::vector<int> X, Y;
}; // Obj
As you can see, as you increase the number of parameters, the number of necessary constructors grow in permutations.
If you don't have a concrete type but have a templatized constructor, you can use perfect-forwarding like so:
class Obj {
public:
template <typename T, typename U>
Obj(T &&x, U &&y)
: X(std::forward<T>(x)), Y(std::forward<U>(y)) {}
private:
std::vector<int> X, Y;
}; // Obj
References:
Want Speed? Pass by Value
C++ Seasoning
I am answering myself because I will try to summarize some of the answers. How many moves/copies do we have in each case?
(A) Pass by value and move assignment construct, passing a X parameter. If X is a...
Temporary: 1 move (the copy is elided)
Lvalue: 1 copy 1 move
std::move(lvalue): 2 moves
(B) Pass by reference and copy assignment usual (pre C++11) construct. If X is a...
Temporary: 1 copy
Lvalue: 1 copy
std::move(lvalue): 1 copy
We can assume the three kinds of parameters are equally probable. So every 3 calls we have (A) 4 moves and 1 copy, or (B) 3 copies. I.e., in average, (A) 1.33 moves and 0.33 copies per call or (B) 1 copy per call.
If we come to a situation when our classes consist mostly of PODs, moves are as expensive as copies. So we would have 1.66 copies (or moves) per call to the setter in case (A) and 1 copies in case (B).
We can say that in some circumstances (PODs based types), the pass-by-value-and-then-move construct is a very bad idea. It is 66% slower and it depends on a C++11 feature.
On the other hand, if our classes include containers (which make use of dynamic memory), (A) should be much faster (except if we mostly pass lvalues).
Please, correct me if I'm wrong.
Readability in the declaration:
void foo1( A a ); // easy to read, but unless you see the implementation
// you don't know for sure if a std::move() is used.
void foo2( const A & a ); // longer declaration, but the interface shows
// that no copy is required on calling foo().
Performance:
A a;
foo1( a ); // copy + move
foo2( a ); // pass by reference + copy
Responsibilities:
A a;
foo1( a ); // caller copies, foo1 moves
foo2( a ); // foo2 copies
For typical inline code there is usually no difference when optimized.
But foo2() might do the copy only on certain conditions (e.g. insert into map if key does not exist), whereas for foo1() the copy will always be done.
Since const reference is pretty much the same as passing by value but without creating a copy (to my understanding). So is there a case where it is needed to create a copy of the variables (so we would need to use pass by value).
There are situations where you don't modify the input, but you still need an internal copy of the input, and then you may as well take the arguments by value. For example, suppose you have a function that returns a sorted copy of a vector:
template <typename V> V sorted_copy_1(V const & v)
{
V v_copy = v;
std::sort(v_copy.begin(), v_copy.end());
return v;
}
This is fine, but if the user has a vector that they never need for any other purpose, then you have to make a mandatory copy here that may be unnecessary. So just take the argument by value:
template <typename V> V sorted_copy_2(V v)
{
std::sort(v.begin(), v.end());
return v;
}
Now the entire process of producing, sorting and returning a vector can be done essentially "in-place".
Less expensive examples are algorithms which consume counters or iterators which need to be modified in the process of the algorithm. Again, taking those by value allows you to use the function parameter directly, rather than requiring a local copy.
It's usually faster to pass basic data types such as ints, floats and pointers by value.
Your function may want to modify the parameter locally, without altering the state of the variable passed in.
C++11 introduces move semantics. To move an object into a function parameter, its type cannot be const reference.
Like so many things, it's a balance.
We pass by const reference to avoid making a copy of the object.
When you pass a const reference, you pass a pointer (references are pointers with extra sugar to make them taste less bitter). And assuming the object is trivial to copy, of course.
To access a reference, the compiler will have to dereference the pointer to get to the content [assuming it can't be inlined and the compiler optimises away the dereference, but in that case, it will also optimise away the extra copy, so there's no loss from passing by value either].
So, if your copy is "cheaper" than the sum of dereferencing and passing the pointer, then you "win" when you pass by value.
And of course, if you are going to make a copy ANYWAY, then you may just as well make the copy when constructing the argument, rather than copying explicitly later.
The best example is probably the Copy and Swap idiom:
C& operator=(C other)
{
swap(*this, other);
return *this;
}
Taking other by value instead of by const reference makes it much easier to write a correct assignment operator that avoids code duplication and provides a strong exception guarantee!
Also passing iterators and pointers is done by value since it makes those algorithms much more reasonable to code, since they can modify their parameters locally. Otherwise something like std::partition would have to immediately copy its input anyway, which is both inefficient and looks silly. And we all know that avoiding silly-looking code is the number one priority:
template<class BidirIt, class UnaryPredicate>
BidirIt partition(BidirIt first, BidirIt last, UnaryPredicate p)
{
while (1) {
while ((first != last) && p(*first)) {
++first;
}
if (first == last--) break;
while ((first != last) && !p(*last)) {
--last;
}
if (first == last) break;
std::iter_swap(first++, last);
}
return first;
}
A const& cannot be changed without a const_cast through the reference, but it can be changed. At any point where code leaves the "analysis range" of your compiler (maybe a function call to a different compilation unit, or through a function pointer it cannot determine the value of at compilation time) it must assume that the value referred to may have changed.
This costs optimization. And it can make it harder to reason about possible bugs or quirks in your code: a reference is non-local state, and functions that operate only on local state and produce no side effects are really easy to reason about. Making your code easy to reason about is a large boon: more time is spent maintaining and fixing code than writing it, and effort spent on performance is fungible (you can spent it where it matters, instead of wasting time on micro optimizations everywhere).
On the other hand, a value requires that the value be copied into local automatic storage, which has costs.
But if your object is cheap to copy, and you don't want the above effect to occur, always take by value as it makes the compilers job of understanding the function easier.
Naturally only when the value is cheap to copy. If expensive to copy, or even if the copy cost is unknown, that cost should be enough to take by const&.
The short version of the above: taking by value makes it easier for you and the compiler to reason about the state of the parameter.
There is another reason. If your object is cheap to move, and you are going to store a local copy anyhow, taking by value opens up efficiencies. If you take a std::string by const&, then make a local copy, one std::string may be created in order to pass thes parameter, and another created for the local copy.
If you took the std::string by value, only one copy will be created (and possibly moved).
For a concrete example:
std::string some_external_state;
void foo( std::string const& str ) {
some_external_state = str;
}
void bar( std::string str ) {
some_external_state = std::move(str);
}
then we can compare:
int main() {
foo("Hello world!");
bar("Goodbye cruel world.");
}
the call to foo creates a std::string containing "Hello world!". It is then copied again into the some_external_state. 2 copies are made, 1 string discarded.
The call to bar directly creates the std::string parameter. Its state is then moved into some_external_state. 1 copy created, 1 move, 1 (empty) string discarded.
There are also certain exception safety improvements caused by this technique, as any allocation happens outside of bar, while foo could throw a resource exhausted exception.
This only applies when perfect forwarding would be annoying or fail, when moving is known to be cheap, when copying could be expensive, and when you know you are almost certainly going to make a local copy of the parameter.
Finally, there are some small types (like int) which the non-optimized ABI for direct copies is faster than the non-optimized ABI for const& parameters. This mainly matters when coding interfaces that cannot or will not be optimized, and is usually a micro optimization.
Since we have move semantics in C++, nowadays it is usual to do
void set_a(A a) { _a = std::move(a); }
The reasoning is that if a is an rvalue, the copy will be elided and there will be just one move.
But what happens if a is an lvalue? It seems there will be a copy construction and then a move assignment (assuming A has a proper move assignment operator). Move assignments can be costly if the object has too many member variables.
On the other hand, if we do
void set_a(const A& a) { _a = a; }
There will be just one copy assignment. Can we say this way is preferred over the pass-by-value idiom if we will pass lvalues?
Expensive-to-move types are rare in modern C++ usage. If you are concerned about the cost of the move, write both overloads:
void set_a(const A& a) { _a = a; }
void set_a(A&& a) { _a = std::move(a); }
or a perfect-forwarding setter:
template <typename T>
void set_a(T&& a) { _a = std::forward<T>(a); }
that will accept lvalues, rvalues, and anything else implicitly convertible to decltype(_a) without requiring extra copies or moves.
Despite requiring an extra move when setting from an lvalue, the idiom is not bad since (a) the vast majority of types provide constant-time moves and (b) copy-and-swap provides exception safety and near-optimal performance in a single line of code.
But what happens if a is an lvalue? It seems there will be a copy
construction and then a move assignment (assuming A has a proper move
assignment operator). Move assignments can be costly if the object has
too many member variables.
Problem well spotted. I wouldn't go as far as to say that the pass-by-value-and-then-move construct is a bad idiom but it definitely has its potential pitfalls.
If your type is expensive to move and / or moving it is essentially just a copy, then the pass-by-value approach is suboptimal. Examples of such types would include types with a fixed size array as a member: It may be relatively expensive to move and a move is just a copy. See also
Small String Optimization and Move Operations and
"Want speed? Measure." (by Howard Hinnant)
in this context.
The pass-by-value approach has the advantage that you only need to maintain one function but you pay for this with performance. It depends on your application whether this maintenance advantage outweighs the loss in performance.
The pass by lvalue and rvalue reference approach can lead to maintenance headaches quickly if you have multiple arguments. Consider this:
#include <vector>
using namespace std;
struct A { vector<int> v; };
struct B { vector<int> v; };
struct C {
A a;
B b;
C(const A& a, const B& b) : a(a), b(b) { }
C(const A& a, B&& b) : a(a), b(move(b)) { }
C( A&& a, const B& b) : a(move(a)), b(b) { }
C( A&& a, B&& b) : a(move(a)), b(move(b)) { }
};
If you have multiple arguments, you will have a permutation problem. In this very simple example, it is probably still not that bad to maintain these 4 constructors. However, already in this simple case, I would seriously consider using the pass-by-value approach with a single function
C(A a, B b) : a(move(a)), b(move(b)) { }
instead of the above 4 constructors.
So long story short, neither approach is without drawbacks. Make your decisions based on actual profiling information, instead of optimizing prematurely.
The current answers are quite incomplete. Instead, I will try to conclude based on the lists of pros and cons I find.
Short answer
In short, it may be OK, but sometimes bad.
This idiom, namely the unifying interface, has better clarity (both in conceptual design and implementation) compared to forwarding templates or different overloads. It is sometimes used with copy-and-swap (actually, as well as move-and-swap in this case).
Detailed analysis
The pros are:
It needs only one function for each parameter list.
It needs indeed only one, not multiple ordinary overloads (or even 2n overloads when you have n parameters when each one can be unqualified or const-qualified).
Like within a forwarding template, parameters passed by value are compatible with not only const, but volatile, which reduce even more ordinary overloads.
Combined with the bullet above, you don't need 4n overloads to serve to {unqulified, const, const, const volatile} combinations for n parameters.
Compared to a forwarding template, it can be a non-templated function as long as the parameters are not needed to be generic (parameterized through template type parameters). This allows out-of-line definitions instead of template definitions needed to be instantiated for each instance in each translation unit, which can make significant improvement to translation-time performance (typically, during both compiling and linking).
It also makes other overloads (if any) easier to implement.
If you have a forwarding template for a parameter object type T, it may still clash with overloads having a parameter const T& in the same position, because the argument can be a lvalue of type T and the template instantiated with type T& (rather than const T&) for it can be more preferred by the overloading rule when there is no other way to differentiate which is the best overloading candidate. This inconsistency may be quite surprising.
In particular, consider you have forwarding template constructor with one parameter of type P&& in a class C. How many time will you forget to excluded the instance of P&& away from possibly cv-qualified C by SFINAE (e.g. by adding typename = enable_if_t<!is_same<C, decay_t<P>> to the template-parameter-list), to ensure it does not clash with copy/move constructors (even when the latter are explicitly user-provided)?
Since the parameter is passed by value of a non-reference type, it can force the argument be passed as a prvalue. This can make a difference when the argument is of a class literal type. Consider there is such a class with a static constexpr data member declared in some class without an out-of-class definition, when it is used as an argument to a parameter of lvalue reference type, it may eventually fail to link, because it is odr-used and there is no definition of it.
Note since ISO C++ 17 the rules of static constexpr data member have changed to introduce a definition implicitly, so the difference is not significant in this case.
The cons are:
A unifying interface can not replace copy and move constructors where the parameter object type is identical to the class. Otherwise, copy-initialization of the parameter would be infinite recursion, because it will call the unifying constructor, and the constructor then call itself.
As mentioned by other answers, if the cost of copy is not ignorable (cheap and predictable enough), this means you will almost always have the degeneration of performance in the calls when the copy is not needed, because copy-initialization of a unifying passed-by-value parameter unconditionally introduce a copy (either copied-to or moved-to) of the argument unless elided.
Even with mandatory elision since C++17, copy-initialization of a parameter object is still hardly free to be removed away - unless the implementation try very hard to prove the behavior not changed according to as-if rules instead of the dedicated copy elision rules applicable here, which might be sometimes impossible without a whole program analysis.
Likewise, the cost of destruction may not be ignorable as well, particularly when non-trivial subobjects are taken into account (e.g. in cases of containers). The difference is that, it does not only apply to the copy-initialization introduced by the copy construction, but also by the move construction. Making move cheaper than copy in constructors can not improve the situation. The more cost of copy-initialization, the more cost of destruction you have to afford.
A minor shortcoming is that there is no way to tweak the interface in different ways as plural overloads, for example, specifying different noexcept-specifiers for parameters of const& and && qualified types.
OTOH, in this example, unifying interface will usually provide you with noexcept(false) copy + noexcept move if you specifies noexcept, or always noexcept(false) when you specify nothing (or explicit noexcept(false)). (Note in the former case, noexcept does not prevent throwing during copy because that will only occur during evaluation of arguments, which is out of the function body.) There is no further chance to tune them separately.
This is considered minor because it is not frequently needed in reality.
Even if such overloads are used, they are probably confusing by nature: different specifiers may hide subtle but important behavioral differences which are difficult to reason about. Why not different names instead of overloads?
Note the example of noexcept may be particularly problematic since C++17 because noexcept-specification now affect the function type. (Some unexpected compatibility issues can be diagnosed by Clang++ warning.)
Sometimes the unconditional copy is actually useful. Because composition of operations with strong-exception guarantee does not hold the guarantee in nature, a copy can be used as a transactional state holder when the strong-exception guarantee is required and the operation cannot be broken down as sequence of operations with no less strict (no-exception or strong) exception guarantee. (This includes the copy-and-swap idiom, although assignments are not recommended to be unified for other reasons in general, see below.) However, this does not mean the copy is otherwise unacceptable. If the intention of the interface is always to create some object of type T, and the cost of moving T is ignorable, the copy can be moved to the target without unwanted overhead.
Conclusions
So for some given operations, here are suggestions about whether using a unifying interface to replace them:
If not all of the parameter types match the unifying interface, or if there is behavioral difference other than the cost of new copies among operations being unified, there cannot be a unifying interface.
If the following conditions are failed to be fit for all parameters, there cannot be a unifying interface. (But it can still be broken down to different named-functions, delegating one call to another.)
For any parameter of type T, if a copy of each argument is needed for all operations, use unifying.
If both copy and move construction of T have ignorable cost, use unifying.
If the intention of the interface is always to create some object of type T, and the cost of the move construction of T is ignorable, use unifying.
Otherwise, avoid unifying.
Here are some examples need to avoid unifying:
Assignment operations (including assignment to the subobjects thereof, typically with copy-and-swap idiom) for T without ignorable cost in copy and move constructions does not meet the criteria of unifying, because the intention of assignment is not to create (but to replace the content of) the object. The copied object will eventually be destructed, which incurs unnecessary overhead. This is even more obvious for cases of self-assignment.
Insertion of values to a container does not meet the criteria, unless both the copy-initialization and destruction have ignorable cost. If the operation fails (due to the allocation failure, duplicate values or so on) after copy-initialization, the parameters have to be destructed, which incurs unnecessary overhead.
Conditionally creation of object based on parameters will incur the overhead when it does not actually create the object (e.g. std::map::insert_or_assign-like container insertion even in spite of the failure above).
Note the accurate limit of "ignorable" cost is somewhat subjective because it eventually depends on how much cost can be tolerated by the developers and/or the users, and it may vary case by case.
Practically, I (conservatively) assume any trivially copyable and trivailly destructible type whose size is not more than one machine word (like a pointer) qualifying the criteria of ignorable cost in general - if the resulted code actually cost too much in such case, it suggests either a wrong configuration of the build tool is used, or the toolchain is not ready for production.
Do profile if there is any further doubt on performance.
Additional case study
There are some other well-known types preferred to be passed by value or not, depending on the conventions:
Types need to preserve reference values by convention should not be passed by value.
A canonical example is the argument forwarding call wrapper defined in ISO C++, which requires to forward references. Note in the caller position it may also preserve the reference respecting to the ref-qualifier.
An instance of this example is std::bind. See also the resolution of LWG 817.
Some generic code may directly copy some parameters. It may be even without std::move, because the cost of the copy is assumed to be ignorable and a move does not necessarily make it better.
Such parameters include iterators and function objects (except the case of argument forwarding caller wrappers discussed above).
Note the constructor template of std::function (but not the assignment operator template) also uses the pass-by-value functor parameter.
Types presumably having the cost comparable to pass-by-value parameter types with ignorable cost are also preferred to be pass-by-value. (Sometimes they are used as dedicated alternatives.) For example, instances of std::initializer_list and std::basic_string_view are more or less two pointers or a pointer plus a size. This fact makes them cheap enough to be directly passed without using references.
Some types should be better avoided passed by value unless you do need a copy. There are different reasons.
Avoid copy by default, because the copy may be quite expensive, or at least it is not easy to guarantee the copy is cheap without some inspection of the runtime properties of the value being copied. Containers are typical examples in this sort.
Without statically knowing how many elements in a container, it is generally not safe (in the sense of a DoS attack, for example) to be copied.
A nested container (of other containers) will easily make the performance problem of copying worse.
Even empty containers are not guaranteed cheap to be copied. (Strictly speaking, this depends on the concrete implementation of the container, e.g. the existence of the "sentinel" element for some node-based containers... But no, keep it simple, just avoid copying by default.)
Avoid copy by default, even when the performance is totally uninterested, because there can be some unexpected side effects.
In particular, allocator-awared containers and some other types with similar treatment to allocators ("container semantics", in David Krauss' word), should not be passed by value - allocator propagation is just another big semantic worm can.
A few other types conventionally depend. For example, see GotW #91 for shared_ptr instances. (However, not all smart pointers are like that; observer_ptr are more like raw pointers.)
For the general case where the value will be stored, the pass-by-value only is a good compromise-
For the case where you know that only lvalues will be passed (some tightly coupled code) it's unreasonable, unsmart.
For the case where one suspects a speed improvement by providing both, first THINK TWICE, and if that didn't help, MEASURE.
Where the value will not be stored I prefer the pass by reference, because that prevents umpteen needless copy operations.
Finally, if programming could be reduced to unthinking application of rules, we could leave it to robots. So IMHO it's not a good idea to focus so much on rules. Better to focus on what the advantages and costs are, for different situations. Costs include not only speed, but also e.g. code size and clarity. Rules can't generally handle such conflicts of interest.
Pass by value, then move is actually a good idiom for objects that you know are movable.
As you mentioned, if an rvalue is passed, it'll either elide the copy, or be moved, then within the constructor it will be moved.
You could overload the copy constructor and move constructor explicitly, however it gets more complicated if you have more than one parameter.
Consider the example,
class Obj {
public:
Obj(std::vector<int> x, std::vector<int> y)
: X(std::move(x)), Y(std::move(y)) {}
private:
/* Our internal data. */
std::vector<int> X, Y;
}; // Obj
Suppose if you wanted to provide explicit versions, you end up with 4 constructors like so:
class Obj {
public:
Obj(std::vector<int> &&x, std::vector<int> &&y)
: X(std::move(x)), Y(std::move(y)) {}
Obj(std::vector<int> &&x, const std::vector<int> &y)
: X(std::move(x)), Y(y) {}
Obj(const std::vector<int> &x, std::vector<int> &&y)
: X(x), Y(std::move(y)) {}
Obj(const std::vector<int> &x, const std::vector<int> &y)
: X(x), Y(y) {}
private:
/* Our internal data. */
std::vector<int> X, Y;
}; // Obj
As you can see, as you increase the number of parameters, the number of necessary constructors grow in permutations.
If you don't have a concrete type but have a templatized constructor, you can use perfect-forwarding like so:
class Obj {
public:
template <typename T, typename U>
Obj(T &&x, U &&y)
: X(std::forward<T>(x)), Y(std::forward<U>(y)) {}
private:
std::vector<int> X, Y;
}; // Obj
References:
Want Speed? Pass by Value
C++ Seasoning
I am answering myself because I will try to summarize some of the answers. How many moves/copies do we have in each case?
(A) Pass by value and move assignment construct, passing a X parameter. If X is a...
Temporary: 1 move (the copy is elided)
Lvalue: 1 copy 1 move
std::move(lvalue): 2 moves
(B) Pass by reference and copy assignment usual (pre C++11) construct. If X is a...
Temporary: 1 copy
Lvalue: 1 copy
std::move(lvalue): 1 copy
We can assume the three kinds of parameters are equally probable. So every 3 calls we have (A) 4 moves and 1 copy, or (B) 3 copies. I.e., in average, (A) 1.33 moves and 0.33 copies per call or (B) 1 copy per call.
If we come to a situation when our classes consist mostly of PODs, moves are as expensive as copies. So we would have 1.66 copies (or moves) per call to the setter in case (A) and 1 copies in case (B).
We can say that in some circumstances (PODs based types), the pass-by-value-and-then-move construct is a very bad idea. It is 66% slower and it depends on a C++11 feature.
On the other hand, if our classes include containers (which make use of dynamic memory), (A) should be much faster (except if we mostly pass lvalues).
Please, correct me if I'm wrong.
Readability in the declaration:
void foo1( A a ); // easy to read, but unless you see the implementation
// you don't know for sure if a std::move() is used.
void foo2( const A & a ); // longer declaration, but the interface shows
// that no copy is required on calling foo().
Performance:
A a;
foo1( a ); // copy + move
foo2( a ); // pass by reference + copy
Responsibilities:
A a;
foo1( a ); // caller copies, foo1 moves
foo2( a ); // foo2 copies
For typical inline code there is usually no difference when optimized.
But foo2() might do the copy only on certain conditions (e.g. insert into map if key does not exist), whereas for foo1() the copy will always be done.