Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to improve std::vector parameter passing (move semantics?)

It seems that I cannot completely understand move semantics: I want to fill an std::vector (member of a class) from an external function. Currently, I have something like:

void fillVector(MyClass & myclass) {
    std::vector<int> vec;
    /* Filling vec */
    // ...
    myclass.setVector(vec);
}

class MyClass {
    public:
         setVector(const std::vector<int> & v) { v_ = v;}
    private:
         std::vector<int> v_;
};

int main() {
    MyClass myclass;
    fillVector(myclass);
    /* Use myclass.v_ somehow */.
}

I had this code for a long time, and it works fine. Now, I cannot understand how it works since I am asigning a reference to a vector which will be destroyed. My questions are:

  1. How is this code able to work?
  2. What is the best way to implement fillVector and MyClass::setVector()? I think that can be done with move semantics but I cannot figure out how.

Thank you!

like image 681
Javi Avatar asked Mar 16 '23 19:03

Javi


2 Answers

Why the code works:

The scope of vec is the body of fillVector. Inside this entire scope, vec is perfectly valid. This includes the call to setVector. v (the parameter of setVector) is a reference which binds to vec. The body of setVector copies the contents of the vector vec (accessed as v) into v_. That's an actual copy of data, no reference assignment. Only after setVector ends does fillVector end, at which point vec is destroyed. But it was already copied, so all is well.

How to use move semantics:

You could provide an extra overload of setVector which will take an rvalue reference:

class MyClass {
public:
  // ...
  void setVector(std::vector<int> &&v) { v_ = std::move(v); }
  // ...
};

Then, move vec into setVector:

void fillVector(MyClass & myclass) {
    std::vector<int> vec;
    /* Filling vec */
    // ...
    myclass.setVector(std::move(vec));
}

This will invoke the rvalue overload instead of the lvalue one, and the data will be moved, not copied.

However, perhaps a better interface would be to refactor fillVector so that it returns the vector by value, using move semantics automatically:

std::vector<int> fillVector() {
    std::vector<int> vec;
    /* Filling vec */
    // ...
    return vec;  // Note that a move automatically happens here, or even NRVO
}

int main() {
    MyClass myclass;
    myclass.setVector(fillVector());
    /* Use myclass.v_ somehow */.
}
like image 168
Angew is no longer proud of SO Avatar answered Mar 19 '23 09:03

Angew is no longer proud of SO


vector::operator= assigns contents, and doesn't just link to existing items. When you do v_ = v, v_ gets a new copy of everything in v. Once v is destroyed, the items in v_ still exist.

An easy way to test this and see this in action is to make changes to one of the items in v_[i] and see then print out both v[i] and v_[i]. They will be different.

I don't see anything wrong with your implementation of setVector and fillVector. Is there something in particular you're trying to achieve?

like image 20
Rubix Rechvin Avatar answered Mar 19 '23 09:03

Rubix Rechvin