Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why should arguments be passed by value when used to initialize another object?

Tags:

c++

c++11

When passing objects to functions there is the choice to pass arguments either by value or by const&. Especially when the object is possibly expensive to create and it is internally mutated or used to initialize another object the recommendation is to pass the object by value. For example:

class Foo {
    std::vector<std::string> d_strings;
public:
    Foo(std::vector<std::string> strings): d_strings(std::move(strings)) {}
    // ...
};

The conventional approach would be to declare the strings parameter as std::vector<std::string> const& and copy the argument. The value argument to the constructor above also needs to be copied!

Why is it preferable to pass by value rather than pass by const&?

like image 521
Dietmar Kühl Avatar asked Nov 24 '15 10:11

Dietmar Kühl


1 Answers

When passing the strings argument by const& there is a guaranteed copy: there is no other way to get hold a, well, copy of the argument other than copying it. The question becomes: how is that different when passing by value?

When the argument is passed by value the strings object is clearly used nowhere else and its content can be moved. Move construction of expansive to copy objects may still be comparatively cheap. For example, in the case of the std::vector<std::string> the move is just copying a few pointers to the new object and setting a few pointers to indicate to the original object that it shouldn't release anything.

There is still the need to create the argument, though. However, the creation of the argument may be elided without creating a new object. For example

Foo f(std::vector<std::string>{ "one", "two", "three" });

will create a vector with three strings and construction of the argument to the Foo construct is most likely elided. In the worst case, the argument is constructed by moving the temporary vector, avoiding a copy, too.

There are, of course, cases where a copy still needs to be created. For example, in the case

std::vector<std::string> v{ "one", "two", "three" };
Foo                      f(v);

The argument is created by a copy. The ready made copy is then moved to the member. In this case pass by const& would have been better because only a copy construction would have been needed rather than a copy construction (to create the argument) followed by a move construction (to create the member) being done.

That is, passing by value enables possibly eliding a copy entirely and just having a move. In the worst case, when the argument needs to be copied anyway, an additional move needs to be performed. Since the move is generally assumed to be a cheap operation the expectation is that overall pass by value for objects which need to be transferred results in better performance.

like image 68
Dietmar Kühl Avatar answered Oct 15 '22 11:10

Dietmar Kühl