How does a default move ctor deal with pointers?

Hi,

Depending on how the compiler implements the default move ctor, this code could duplicate the pointer elements!!

1
2
3
4
5
6
7
8
9
class Vector
{
Vector(int sz) : elements{new double[sz]}, size{sz} {}
Vector(Vector&& v) = default;     // does it copy elements pointer??
~Vector() { delete [] elements; } // does it delete more than once??

int size;
double* elements;
};



1
2
3
4
5
6
7
8
9
friend Vector operator+(const Vector& lhs, const Vector&rhs )
{
	Vector vec{ lhs.sz };
	for(int i=0; i < lhs.sz; ++i)
	{
		vec[i] = lhs[i] +rhs[i];
	}
	return std::move(vec);
}


The compiler generates an implicit move-constructor for you, only if:
· there are no user-declared copy constructors;
· there are no user-declared copy assignment operators;
· there are no user-declared move assignment operators;
· there is no user-declared destructor.

It means, that if you have a user-declared destructor, as in your example, then you must implement the move constructor yourself.

This makes sense, as otherwise you could easily get a "double free" error – assuming that the compiler-generated (implicit) move constructor simply copies the pointer value, whereas the user-provided destructor deletes the pointer.

If you implement the move constructor yourself, then you are responsible for setting the pointer to NULL in the "old" object, after moving (copying) the pointer value to the "new" object, so that a "double free" error is avoided.


If an implicit move-constructor is generated by the compiler, then a "trivial" move-constructor is generated, if the following holds true:
· it is not user-provided (meaning, it is implicitly-defined or defaulted);
· T has no virtual member functions;
· T has no virtual base classes;
· the move constructor selected for every direct base of T is trivial;
· the move constructor selected for every non-static class type (or array of class type) member of T is trivial.

Also note:
A trivial move constructor is a constructor that performs the same action as the trivial copy constructor, that is, makes a copy of the object representation as if by std::memmove.

This means that a "raw" pointer would simply be copied...

Otherwise, if the implicitly-defined move constructor is not a "trivial" one, then the following applies:
For non-union class types (class and struct), the move constructor performs full member-wise move of the object's bases and non-static members, in their initialization order, using direct initialization with an xvalue argument.
Last edited on
Compiler-generated move constructors copy trivial sub-objects and move non-trivial ones.

In your sample code, return std::move(vec) prevents the compiler from applying named return value optimization. Just return vec.
Last edited on
Topic archived. No new replies allowed.