Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why std::move don't change source variable to default value in default move constructor?

I try to understand the move constructor.

I allocate memory in the class' constructor and destroy it in the destructor.

When I try to move the class, I still have a double free.

#include <algorithm>

class TestClass
{
 public:
  TestClass() {a_ = new int[1];}
  TestClass(TestClass const& other) = delete;
  TestClass(TestClass && other) noexcept // = default;
  {
    this->a_ = std::move(other.a_);
  }
  ~TestClass() {delete[] a_;}
 private:
  int* a_ = nullptr;
};

int main( int argc, char** argv )
{
  TestClass t;
  TestClass t2 = std::move(t);
}

Why std::move do not change to nullptr other.a_ ?

I have the same problem if the move constructor is default.

I found the following questions but I still don't know why the move operator don't change the source variable to default value.

How does std::move invalidates the value of original variable?

C++ how to move object to a nullptr

C++ std::move a pointer

like image 493
Vincent LE GARREC Avatar asked Jan 18 '26 21:01

Vincent LE GARREC


2 Answers

std::move just produces an rvalue (xvalue); it won't perform move operation, it won't modify the argument at all.

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.

Given this->a_ = std::move(other.a_);, as built-in type, i.e. int*, this->a_ is just copy-assigned from ohter.a_, then both the pointers point to the same object. The defaulted move constructor does the same thing in fact. (It performs member-wise move operation on the data members; note that for built-in types the effect of move is same as copy.)

You need to set other.a_ to nullptr explicitly if you want to define that after moved the object should contain a null pointer.

E.g.

TestClass(TestClass && other) noexcept
{
  this->a_ = other.a_;
  other.a_ = nullptr;
}
like image 83
songyuanyao Avatar answered Jan 20 '26 11:01

songyuanyao


First, std::move is just a cast which leads to other.a_ to be treated as an rvalue. For pointers, a move is just a copy.

This is so, I presume, because clearing the source pointer is not necessary in all cases and it would cause overhead in the cases where it's not needed.

You need to do the clearing explicitly.

Or, even simpler, just use std::unique_ptr<int> a_. Then you don't need to define any special member functions and the class behaves as you would imagine.

like image 44
Mika Fischer Avatar answered Jan 20 '26 09:01

Mika Fischer



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!