Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++ What is the best way of allowing moving data out of a class member (std::move syntax)

Imagine that I have a class whose main responsibility is to populate a data container. I would like to move the data out of this class. So I have:

class cCreator
{  
public: 
   void generate()
   {
      ///generate the content of data_ and populate it
      ....
      ....
   }

   //variation 1
   std::vector<int>&& getData1()
   {
      return std::move(data_);
   }

   //variation 2
   std::vector<int> getData2()
   {
      return std::move(data_);    
   }
private:
      std::vector<int> data_
};

S what is the difference between variation 1 and variation 2 of getData(). What changes when I omit && from the function definition??

like image 767
mecahi Avatar asked Mar 04 '16 14:03

mecahi


People also ask

What is the use of std :: move?

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object. In particular, std::move produces an xvalue expression that identifies its argument t . It is exactly equivalent to a static_cast to an rvalue reference type.

Should you use std :: move?

A: You should use std::move if you want to call functions that support move semantics with an argument which is not an rvalue (temporary expression).

Is std :: move faster?

Std::move is not faster than straight up copying. So as the title says; i have an implementation where i copy big objects and thought std::move would give a huge performance boost over straight up copying, but it didn't (was roughly the same time).

Where is std :: move defined?

Move Constructor And Semantics: std::move() is a function used to convert an lvalue reference into the rvalue reference. Used to move the resources from a source object i.e. for efficient transfer of resources from one object to another. std::move() is defined in the <utility> header.


3 Answers

In the first case, no move actually happens in the function itself. You are just returning an rvalue reference, which allows the original caller to move if needed. In the second case, the data is actually being moved into a temporary, regardless of how the caller uses the result.

cCreator c1, c2;
c1.getData1(); // no move
std::vector<int> v1 = c1.getData1(); // move directly from data_
                                     // into v1.
c2.getData2(); // move to temporary, which isn't used.
std::vector<int> v2 = c2.getData1(); // move from data_ to temporary,
                                     // then move from temporary to v2.
like image 196
Vaughn Cato Avatar answered Sep 21 '22 23:09

Vaughn Cato


Version 1 is probably a bad idea, things like f(x.getData()) might be moving or not depending on the signature of f. In general we want any move to be explicit in the code, so you should at very least have something like:

std::vector<int>&& getData() &&
{
  return std::move(data_);
}

So that now the move has to be explicit: f( std::move(x).getData() ), and you probably want to provide an overload for const & just for observing.

Version 2 would be ok if it had another name. In my experience, 99% of the people perceive getX as a non-modifying operation. Better call it releaseData, which is consistent with the naming of unique_ptr::release.

like image 35
sbabbi Avatar answered Sep 22 '22 23:09

sbabbi


I guess in this specific case where the container whose data is extracted (moved) is a stl vector, std::swap would be a valid option. So simply supplying:

const std::vector<int>& getData() const;
std::vector<int>& getData();

woudl enable user to safely extract the data if he so wishes. That might be only viable option if C++11 is not available for some reason.

like image 20
mecahi Avatar answered Sep 22 '22 23:09

mecahi