I am trying to learn move semantics in modern C++. It is my understanding that this construct is useful when designing a class which manages a resource. I tried to experiment with this by building a very simple wrapper around C-style arrays, as follows.
// A wrapper around int[] to practice with the rule of three/five
#include <algorithm>
#include <cstddef>
#include <iostream>
class Vector {
public:
Vector(const std::size_t n) : size_(n), data_(new int[n]) {
std::cout << "called constructor" << std::endl; }
~Vector() { delete[] data_;
std::cout << "called destructor" << std::endl; }
Vector(const Vector&);
Vector& operator=(Vector);
friend void swap(Vector&, Vector&);
std::size_t size() { return size_; }
int& operator[](std::size_t i) { if (i >= size_) throw "out of bounds"; return data_[i]; }
int operator[](std::size_t i) const { if (i >= size_) throw "out of bounds"; return data_[i]; }
// move constructor
Vector(Vector&& v);
// move assignment not needed (see current copy assignment)
//Vector& operator=(Vector&&);
private:
std::size_t size_;
int* data_;
};
void swap(Vector& a, Vector& b) {
std::swap(a.size_, b.size_);
std::swap(a.data_, b.data_);
}
Vector::Vector(const Vector& v) :
size_(v.size_),
data_(new int[v.size_])
{
std::copy(v.data_, v.data_ + v.size_, data_);
std::cout << "called copy constructor" << std::endl;
}
Vector::Vector(Vector&& v) :
Vector(v.size_)
{
swap(*this, v);
std::cout << "called move constructor" << std::endl;
}
Vector& Vector::operator=(Vector v) {
std::cout << "called copy assignement" << std::endl;
swap(*this, v);
return *this;
}
int main() {
Vector a(2);
Vector b(6);
for (std::size_t i = 0; i < b.size(); ++i) {
b[i] = static_cast<int>(i);
}
a = b;
Vector c(b);
Vector d(Vector(3));
}
I would expect that the last line of code, namely Vector d(Vector(3))
would call the move constructor, as I initialise d with an rvalue (or at least I think this is what I am trying to do here).
However, this is the output when executing the program:
called constructor
called constructor
called copy constructor
called copy assignement
called destructor
called copy constructor
called constructor
called destructor
called destructor
called destructor
called destructor
It seems that the code is never calling the move constructor. Why is this happening? How can I change my code so that it takes advantage of the move constructor?
I also tried changing the implementation of the move constructor as shown in this answer, namely:
Vector::Vector(Vector&& v) :
size_(std::move(v.size_)),
data_(std::move(v.data_))
{
v.size_ = 0;
v.data_ = nullptr;
std::cout << "called move constructor" << std::endl;
}
But still the move constructor is not used.