Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Const reference VS move semantics

I was wondering in which situations I still need to use const references in parameters since C++11. I don't fully understand move semantics but I think this is a legit question. This question is meant for only the situations where a const reference replaces a copy being made while it is only needed to "read" the value (e.g. usage of const member functions).

Normally I would write a (member)function like this:

#include <vector>  template<class T> class Vector {     std::vector<T> _impl; public:     void add(const T& value) {          _impl.push_back(value);     } }; 

But I'm thinking that it's safe to assume that to compiler would optimize it using move semantics if I write it like this and class T ofcourse implements a move constructor:

#include <vector>  template<class T> class Vector {     std::vector<T> _impl; public:     void add(T value) {          _impl.push_back(value);     } }; 

Am I right? If so, is it safe to assume it can be used in any situation? If not, I would like to know which. This would make life much easier since I wouldn't have to implement an class specialization for fundamental types for example, besides it looks much cleaner.

like image 394
Tim Avatar asked Jul 14 '13 18:07

Tim


People also ask

What is a move semantic?

Move semantics allows you to avoid unnecessary copies when working with temporary objects that are about to evaporate, and whose resources can safely be taken from that temporary object and used by another.

What is the difference between pass by reference and pass by const reference?

From what I understand: when you pass by value, the function makes a local copy of the passed argument and uses that; when the function ends, it goes out of scope. When you pass by const reference, the function uses a reference to the passed argument that can't be modified.

Should move constructor be const?

A move constructor should normally take a non-const reference. If it were possible to move from a const object it would usually imply that it was as efficient to copy an object as it was to "move" from it. At this point there is normally no benefit to having a move constructor.

Can you Std move const reference?

"std::move" should only be used where moving can happenWhen passing the result of std::move as a const reference argument. In this case, no object will be moved since it's impossible to call the move constructor from within the function.


1 Answers

The solution you propose:

void add(T value) {      _impl.push_back(value); } 

Would require some adjustment, since this way you do always end up performing one copy of value, even if you pass an rvalue to add() (two copies if you pass an lvalue): since value is an lvalue, the compiler won't automatically move from it when you pass it as an argument to push_back.

Instead, you should do this:

void add(T value) {      _impl.push_back(std::move(value)); //                   ^^^^^^^^^ } 

This is better, but still not sufficiently good for template code, because you do not know if T is cheap or expensive to move. If T is a POD like this:

struct X {     double x;     int i;     char arr[255]; }; 

Then moving it won't be any faster than copying it (in fact, moving it would be the same thing as copying it). Because your generic code is supposed to avoid unnecessary operations (and that's because those operations may be expensive for some types), you cannot afford taking the parameter by value.

One possible solution (the one adopted by the C++ standard library) is to provide two overloads of add(), one taking an lvalue reference and one taking an rvalue reference:

void add(T const& val) { _impl.push_back(val); } void add(T&& val) { _impl.push_back(std::move(val)); } 

Another possibility is to provide a (possibly SFINAE-constrained) perfect-forwarding template version of add() that would accept a so-called universal reference (non-standard term coined by Scott Meyers):

template<typename U> void add(U&& val) { _impl.push_back(std::forward<U>(val)); } 

Both these solutions are optimal in the sense that only one copy is performed when lvalues are provided, and only one move is performed when rvalues are provided.

like image 124
Andy Prowl Avatar answered Oct 21 '22 13:10

Andy Prowl



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!