C++ class member rvalue assignment - c++

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();

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.

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

why does insert in std::unordered_set call the copy constructor?

I have an std::unordered_set of pointers. If I do
int main()
{
std::unordered_set<std::unique_ptr<int>> set;
set.insert(std::make_unique(3));
}
everything works and insert calls the move constructor (i think). However I have a different situation that is similar to the following code
std::unordered_set<std::unique_ptr<int>> set;
void add(const std::unique_ptr<int>& obj) {set.insert(obj);}
int main
{
std::unique_ptr<int> val = std::make_unique<int>(3);
add(std::move(val));
}
and this does not compile. It gives
State Error C2280 'std::unique_ptr<int,std::default_delete>::unique_ptr(const std::unique_ptr<int,std::default_delete> &)': attempting to reference a deleted function
which means that it's trying to call the copy constructor (right?). So the question is: why insert doesn't move the object when it is inside a function? I tried also passing the pointer to the unique_ptr and even adding std::move pretty much everywhere but the copy constructor is always called
void add(const std::unique_ptr<int>& obj)
It comes down to something fundamental: you cannot move a constant object, by definition. "Move" basically means ripping the guts out of the moved-from object, and shoving all the guts into the new, moved-to object (in the most efficient manner possible, with the precision of a surgeon). And you can't do it if the moved-from object is const. It's untouchable. It must remain in its original, pristine condition.
You should be able to do that if the parameter is an rvalue reference:
void add(std::unique_ptr<int>&& obj) {set.insert(std::move(obj));}
std::unique_ptr by definition does not have a copy constructor. It does have a move constructor. But your unique pointer here is a const, so the move constructor cannot be called

Does D have a move constructor?

I am referencing this SO answer Does D have something akin to C++0x's move semantics?
Next, you can override C++'s constructor(constructor &&that) by defining this(Struct that). Likewise, you can override the assign with opAssign(Struct that). In both cases, you need to make sure that you destroy the values of that.
He gives an example like this:
// Move operations
this(UniquePtr!T that) {
this.ptr = that.ptr;
that.ptr = null;
}
Will the variable that always get moved? Or could it happen that the variable that could get copied in some situations?
It would be unfortunate if I would only null the ptr on a temporary copy.
Well, you can also take a look at this SO question:
Questions about postblit and move semantics
The way that a struct is copied in D is that its memory is blitted, and then if it has a postblit constructor, its postblit constructor is called. And if the compiler determines that a copy isn't actually necessary, then it will just not call the postblit constructor and will not call the destructor on the original object. So, it will have moved the object rather than copy it.
In particular, according to TDPL (p.251), the language guarantees that
All anonymous rvalues are moved, not copied. A call to this(this)
is never inserted when the source is an anonymous rvalue (i.e., a
temporary as featured in the function hun above).
All named temporaries that are stack-allocated inside a function and
then returned elide a call to this(this).
There is no guarantee that other potential elisions are observed.
So, in other cases, the compiler may or may not elide copies, depending on the current compiler implementation and optimization level (e.g. if you pass an lvalue to a function that takes it by value, and that variable is never referenced again after the function call).
So, if you have
void foo(Bar bar)
{}
then whether the argument to foo gets moved or not depends on whether it was an rvalue or an lvalue. If it's an rvalue, it will be moved, whereas if it's an lvalue, it probably won't be (but might depending on the calling code and the compiler).
So, if you have
void foo(UniquePtr!T ptr)
{}
ptr will be moved if foo was passed an rvalue and may or may not be moved it it's passed an lvalue (though generally not). So, what happens with the internals of UniquePtr depends on how you implemented it. If UniquePtr disabled the postblit constructor so that it can't be copied, then passing an rvalue will move the argument, and passing an lvalue will result in a compilation error (since the rvalue is guaranteed to be moved, whereas the lvalue is not).
Now, what you have is
this(UniquePtr!T that)
{
this.ptr = that.ptr;
that.ptr = null;
}
which appears to act like the current type has the same members as those of its argument. So, I assume that what you're actually trying to do here is a copy constructor / move constructor for UniquePtr and not a constructor for an arbitrary type that takes a UniquePtr!T. And if that's what you're doing, then you'd want a postblit constructor - this(this) - and not one that takes the same type as the struct itself (since D does not have copy constructors). So, if what you want is a copy constructor, then you do something like
this(this)
{
// Do any deep copying you want here. e.g.
arr = arr.dup;
}
But if a bitwise copy of your struct's elements works for your type, then you don't need a postblit constructor. But moving is built-in, so you don't need to declare a move constructor regardless (a move will just blit the struct's members). Rather, if what you want is to guarantee that the object is moved and never copied, then what you want to do is disable the struct's postblit constructor. e.g.
#disable this(this);
Then any and all times that you pass a UniquePtr!T anywhere, it's guaranteed to be a move or a compilation error. And while I would have thought that you might have to disable opAssign separately to disable assignment, from the looks of it (based on the code that I just tested), you don't even have to disable assignment separately. Disabling the postblit constructor also disables the assignment operator. But if that weren't the case, then you'd just have to disable opOpAssign as well.
JMD answer covers theoretical part of move semantics, I can extend it with a very simplified example implementation:
struct UniquePtr(T)
{
private T* ptr;
#disable this(this);
UniquePtr release()
{
scope(exit) this.ptr = null;
return UniquePtr(this.ptr);
}
}
// some function that takes argument by value:
void foo ( UniquePtr!int ) { }
auto p = UniquePtr!int(new int);
// won't compile, postblit constructor is disabled
foo(p);
// ok, release() returns a new rvalue which is
// guaranteed to be moved without copying
foo(p.release());
// release also resets previous pointer:
assert(p.ptr is null);
I think I can answer it myself. Quoting the "The Programming Language":
All anonymous rvalues are moved, not copied. A call to this ( this ) is never inserted
when the source is an anonymous rvalue (i.e., a temporary as featured in
the function hun above).
If I understood it correctly, this means that this(Struct that) will never be a copy, because it only accepts rvalues in the first place.

move constructors for vectors of shared_ptr<MyClass>

I understand if you wish to pass a vector of MyClass objects and it is a temporary variable, if there is a move constructor defined for MyClass then this will be called, but what happens if you pass a vector of boost::shared_ptr<MyClass> or std::shared_ptr<MyClass>? Does the shared_ptr have a move constructor which then call's MyClass's move constructor?
if there is a move constructor defined for MyClass then this will be called
Usually not. Moving a vector is usually done my transferring ownership of the managed array, leaving the moved-from vector empty. The objects themselves aren't touched. (I think there may be an exception if the two vectors have incompatible allocators, but that's beyond anything I've ever needed to deal with, so I'm not sure about the details there).
Does the shared_ptr have a move constructor which then call's MyClass's move constructor?
No. Again, it has a move constructor which transfers ownership of the MyClass object to the new pointer, leaving the old pointer empty. The object itself is untouched.
Yes, std::shared_ptr<T> has a move constructor, as well as a templated constructor that can move from related shared pointers, but it does not touch the managed object at all. The newly constructed shared pointer shares ownership of the managed object (if there was one), and the moved-from pointer is disengaged ("null").
Example:
struct Base {}; // N.B.: No need for a virtual destructor
struct Derived : Base {};
auto p = std::make_shared<Derived>();
std::shared_ptr<Base> q = std::move(p);
assert(!p);
If you mean moving std::vector<std::shared_ptr<MyClass>>. Then even the move constructor of std::shared_ptr won't be called. Because the move operation is directly done on std::vectorlevel.
For example, a std::vector<T> may be implemented as a pointer to array of T, and a size member. The move constructor for this can be implemented as:
template <typename T>
class vector {
public:
/* ... other members */
vector(vector &&another): _p(another._p), _size(another._size) {
/* Transfer data ownership */
another._p = nullptr;
another._size = 0;
}
private:
T *_p;
size_t _size;
}
You can see in this process, no data member of type T is touched at all.
EDIT: More specially in C++11 Standard: ยง23.2.1. General container requirements (4) there is a table contains requirements on implementations of general containers, which contains following requirements:
(X is the type of the elements, u is an identifier declaration, rv is rvalue reference, a is a container of type X)
X u(rv)
X u = rv
C++ Standard: These two (move constructors) should have constant time complexity for all standard containers except std::array.
So it's easy to conclude implementations must use a way like I pointed above for move constructors of std::vector since it cannot invoke move constructors of individual elements or the time complexity will become linear time.
a = rv
C++ Standard: All existing elements of a are either move assigned to or destroyed a shall be equal to the value that rv had before this assignment.
This is for move assign operator. This sentence only states that original elements in a should be "properly handled" (either move-assigned in or destroyed). But this is not a strict requirement. IMHO implementations can choose the best suited way.
I also looked at code in Visual C++ 2013 and this is the snippet I found (vector header, starting from line 836):
/* Directly move, like code above */
void _Assign_rv(_Myt&& _Right, true_type)
{ // move from _Right, stealing its contents
this->_Swap_all((_Myt&)_Right);
this->_Myfirst = _Right._Myfirst;
this->_Mylast = _Right._Mylast;
this->_Myend = _Right._Myend;
_Right._Myfirst = pointer();
_Right._Mylast = pointer();
_Right._Myend = pointer();
}
/* Both move assignment operator and move constructor will call this */
void _Assign_rv(_Myt&& _Right, false_type)
{ // move from _Right, possibly moving its contents
if (get_allocator() == _Right.get_allocator())
_Assign_rv(_STD forward<_Myt>(_Right), true_type());
else
_Construct(_STD make_move_iterator(_Right.begin()),
_STD make_move_iterator(_Right.end()));
}
In this code the operation is clear: if both this and right operand have the same allocator, it will directly steal contents without doing anything on individual elements. But if they haven't, then move operations of individual elements will be called. At this time, other answers apply (for std::shared_ptr stuff).

Passing a movable object

Reading an answer in SO, he passed a vector with move. I thought the correct way is to pass it simple without using move:
class B
{
std::vector<T> data;
public:
B(std::vector<T> p) : data(std::move(p)) {}
^^^^^^^^^^^^
?
};
The second way is:
class B
{
std::vector<T> data;
public:
B(std::vector<T> p) : data(p) {}
};
Which one is correct?
The function argument is already taken by value, so a copy will already have been made. The local object p is unquestioningly yours and yours alone, so you can move from it unconditionally.
The beauty of taking the argument by value is that it works for both lvalues and rvalues: For lvalues you make a genuine copy, since there's nothing else you can do, but for rvalues the function argument itself can be constructed by moving, so there's only one expensive construction happening ever, and everything else is moved.
When you construct an object from an lvalue, it will be copied. When you construct an object from a non-const rvalue it can be moved (whether it will be moved depends on the class having a move constructor). In the context of your constructor, p is clearly a lvalue: it has a name. However, it is a local variable and about to go away, i.e., it is save to move from it: std::move(p) makes the object appear as if it is an rvalue: in this context, std::move()ing the value is the right way to go.
Note that the recommendations are different if you return an object: a local value returned in a return statement is automatically moved from. Using std::move() would just reinforce the statement but would also inhibit the possibility of eliding the copy/move entirely. In your constructor the copy/move cannot be elided because copy elision only works with temporary objects or with local objects in return statements.
T f() {
T local;
return local; // good: elided, moved, or copied in this order of preference
}
T g() {
T local;
return std::move(local); // bad: can't be elided and will be moved or copied
}

Is there anything incorrect about "return std::move(*this);"?

I know that a certain object is only ever created as a temporary object (it's a private member object within a library). Sometimes, that object is further initialized by chaining member functions together (TempObj().Init("param").Init("other param")). I would like to enable move construction for another object using that temporary instance, and so I was wondering if there was anything incorrect about return std::move(*this).
struct TempObj
{
TempObj &&Member() { /* do stuff */ return std::move(*this); }
};
struct Foo
{
Foo(TempObj &&obj);
};
// typical usage:
Foo foo(TempObj().Member());
Is it functionally equivalent to this?
struct TempObj
{
TempObj(TempObj &&other);
TempObj Member() { /* do stuff */ return *this; }
};
Foo foo(TempObj().Member());
With move semantics, you don't want (or need) to return r-value references from functions ... r-value references are there to "capture" unnamed values or memory addresses. When you return a r-value reference to an object that is in-fact an l-value from the standpoint of the caller, the semantics are all wrong, and you create the opportunity for needless surprises to arise when others are using your object's methods.
In other words, it would be better to orient your code to look like this:
Foo foo(std::move(TempObj().Member()));
and have TempObj::Member simply return a l-value reference. This makes the move explicit, and there are no suprises involved for someone using your object's methods.
Finally, no, it's not functionally or semantically equivalent to your last example. There you are actually making a temporary copy of the object, and that copy will be an r-value object (i.e., an unamed object in this scenario) ... since the assumption with r-value references is that the object is either an unamed value or object, and it can therefore be harmlessly modified by a function you pass it to that takes an r-value reference argument. On the other-hand, if you've passed a copy of an object to a function, then the function cannot modify the original. It will simply modify the referenced temporary r-value, and when the function exits, the temporary r-value copy of the object will be destroyed.