Why pass by value, not by reference const?

Since const reference is almost the same as passing by value, but without creating a copy (in my opinion). So, is there a case where you need to create a copy of variables (so we will need to use pass by value).

+5
source share
5 answers

There are situations when you do not change the input, but you still need an internal copy of the input, and then you can 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 good, but if the user has a vector that they never need for any other purpose, then you should make a mandatory copy here, which may be unnecessary. So just take an argument by value:

 template <typename V> V sorted_copy_2(V v) { std::sort(v.begin(), v.end()); return v; } 

Now the whole process of creating, sorting and returning a vector can be performed essentially “in place”.

Less expensive examples are algorithms that consume counters or iterators, which must be modified during the algorithm. Again, using these values ​​allows you to directly use a function parameter, instead of requiring a local copy.

+12
source
  • It is usually faster to pass basic data types such as int, float, and pointers by value.
  • Your function may want to change the parameter locally without changing the state of the passed variable.
  • C ++ 11 introduces the semantics of movement. To move an object to a function parameter, its type cannot be a const reference.
+4
source

Like many things, this is balance.

We pass the const link to avoid copying the object.

When you pass a link to const, you pass a pointer (links are pointers with extra sugar to make their taste less bitter). And suppose the object is trivial to copy, of course.

To access the link, the compiler will have to dereference the pointer to go to the content [provided that it cannot be embedded and the compiler optimizes the dereferencing, but in this case it also optimizes the extra copy, so there is no loss from passing by value] .

So, if your copy is “cheaper” than the sum of dereferencing and passing the pointer, then you “win” when passing by value.

And, of course, if you are going to make a copy of ANYWAY, then you can also make a copy when building the argument, and not copy explicitly later.

+4
source

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 a reference to const makes it much easier to write the correct assignment operator, which avoids code duplication and provides a reliable guarantee!

Also, the transfer of iterators and pointers is performed by value, since it makes these algorithms more reasonable for coding, since they can change their parameters locally. Otherwise, something like std::partition would have to immediately copy its input anyway, which is inefficient and looks silly. And we all know that avoiding stupid code is 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; } 
+3
source

A const& cannot be changed without const_cast by reference, but it can be changed. At any moment when the code leaves the “analysis range” of your compiler (perhaps calling a function for another compilation module or through a function pointer that cannot determine the value at compile time), it should assume that the value mentioned may have changed.

This cost optimization. And this can make it difficult to reason about possible errors or quirks in your code: the link is a non-local state, and functions that work only in the local state and do not produce any side effects are really easy to reason about. Making code easy to justify is a great blessing: more time is spent on maintaining and fixing the code than writing it, and the effort spent on performance is interchangeable (you can spend it where it's important, and not spend time on micro optimization in all over the world).

On the other hand, a value requires that the value be copied to local automatic storage, which has costs.

But if your object is cheap to copy, and you do not want the aforementioned effect to occur, always take it by value, as this makes it easier to work with compilers to understand the function.

Naturally, only when the value is cheap to copy. If you are expensive, or even if the cost of copying is unknown, this cost should be enough to take on const& .

Short version above: taking by value makes it easier for you and the compiler to talk about the state of the parameter.

There is one more reason. If your object is cheap to move, and you are still going to store a local copy, then when you reach the cost, efficiency increases. If you take std::string to const& , then create a local copy, you can create one std::string to pass the parameter, and the other is created for the local copy.

If you took std::string by default, only one copy will be created (and possibly moved).

For a specific 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."); } 

calling foo creates a std::string containing "Hello world!" . Then it is copied to some_external_state . 2 copies are made, 1 line is discarded.

A call to bar directly creates the std::string parameter. Its state is then moved to some_external_state . 1 copy created, 1 move, 1 (empty) line discarded.

There are also certain security enhancements caused by this technique, since any distribution takes place outside of bar , and foo can throw an exception from the resource.

This is only applicable when a beautiful transfer will annoy or fail, when moving is cheap, when copying can be expensive, and when you know that you are almost certainly going to make a local copy of the parameter.

Finally, there are small types (for example, int ) that are not optimized ABI for direct copies faster than not optimized ABI for const& parameters. This mainly matters when coding interfaces that cannot or will not be optimized, and are usually micro-optimized.

+1
source

Source: https://habr.com/ru/post/1211876/


All Articles