Home > other >  C class member rvalue assignment
C class member rvalue assignment

Time:12-06

I have seen C code that assigns a class member using an std::move call on an rvalue as follows:

class Widget {
  std::vector<int> m_data{};

public:
  // 1. OK
  // x contents are copied locally, m_data space is extended if necessary
  void set_data(const std::vector<int>& x) {
    m_data = x;
  }

  // 2. x creates an rvalue that is moved into m_data. What if Widget is dynamically allocated?
  void set_data(std::vector<int> x) {
    m_data = std::move(x);
  }

  // 3. x is an rvalue generated before the call. What if Widget is dynamically allocated?
  void set_data(std::vector<int>&& x) noexcept {
    m_data = std::move(x);
  }
};

Widget* pW = new Widget{};
pW->setData(std::vector<int>{1, 2, 3});

I don't understand 2 and 3. How is it possible to safely move an rvalue to a class member if the scope of Widget is not the same than the rvalue passed to set_value()?

EDIT: Fixed my code after user17732522 pointed out that the original version was passing an lvalue, not an rvalue to pW->setData();

CodePudding user response:

std::move does not affect the lifetime of the moved object itself. Instead it indicates that ownership over resources owned by the object on which std::move is called may/should be taken over by the function to which std::move(/*...*/) is an argument.

For example for a std::vector moving the object means that the destination vector should take over any dynamic memory allocations made by the source vector containing the vector elements, so that the source vector's state after the move will be that of an empty vector and the state of the destination vector will be that of the source before the move without any allocation or copying of elements needing to take place. The vector objects themselves are not otherwise affected.

std::move on a type that doesn't own any resources, e.g. a simple std::pair<int, float>, has no effect. It will simply result in a copy as without move semantics.

CodePudding user response:

Whether the object is instantiated in dynamic scope, or not, is completely immaterial as far as move semantics getting used, or not, when passing parameters for an invokation of the object's method, and whether or not the object's methods use move semantics inside the method. It is completely irrelevant.

Whether or not undefined behavior results from usage or non-usage of move semantisc will be determined by factors that are not necessarily relevant to the object's scope.

  void set_data(std::vector<int> x) {
    m_data = std::move(x);
  }

In this case, the method caller's is responsible for using move semantics for set_data()'s parameter. Failure to do so will result in the parameter, presumably, getting copy-constructed. Whatever the case may be, it is immaterial when it comes to move-assignment that takes place when assigning m_data from the passed-in parameter. This will happen irregardless of how set_data() gets invoked. The method has no knowledge of how the parameter got passed in.

  void set_data(std::vector<int>&& x) noexcept {
    m_data = std::move(x);
  }

Here, the caller is responsible for producing a movable rvalue reference. Attempting to pass an lvalue, which would produce a loss of move semantics, results in ill-formed code. This, effectively, forces the caller into employing move semantics when calling this method.

How is it possible to safely move an rvalue to a class member if the scope of Widget is not the same than the rvalue passed to set_value()?

Again, it is immaterial. Whether Widgetexists in dynamic scope, or not, is immaterial as far as move semantics related to calling the object's methods. It may or may not indirectly affect whether or not the totality of the object's method calls results in undefined behavior, or not, but the move semantics themselves have no relation to the scope of the object itself.

  • Related