Vincent Le Garrec
Vincent Le Garrec

Reputation: 476

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

Upvotes: 2

Views: 1013

Answers (2)

songyuanyao
songyuanyao

Reputation: 172934

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;
}

Upvotes: 6

Mika Fischer
Mika Fischer

Reputation: 4258

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.

Upvotes: 2

Related Questions