mardi 26 janvier 2021

Move constructor not called when constructing object in function parameter list

I have the following code to test the move constructor in C++. The class MovableArray is designed using the copy and swap idiom. I want to test that move semantics actually works.

However, it seems that the expression in main function with printSize(MovableArray(30)) doesn't invoke the move constructor.

I managed to get the line with 40 invoke move constructor by telling the compiler not to perform copy elision. So I am guessing the reason behind the call with 30 is also due to compiler optimization. However, I didn't find a flag from clang documentation.

Could anyone point out the reason? Thanks.

#include <algorithm>
#include <iostream>
#include <utility>

class MovableArray
{
public:
  MovableArray()
    : size_{ 0 }
    , data_{ nullptr }
  {}
  explicit MovableArray(int size) // ctor
    : size_(size)
    , data_{ new int[size]() } {};
  ~MovableArray() // dtor
  {
    delete[] data_;
    std::cout << "deleted array of size " << size_ << std::endl;
  };
  MovableArray(const MovableArray& other) // copy constructor
    : size_{ other.size_ }
    , data_{ new int[size_]() }
  {
    std::copy(other.data_, other.data_ + other.size_, this->data_);
    std::cout << "Array of size " << size_ << " is copied\n";
  }

  MovableArray(MovableArray&& other) // move constructor
    : MovableArray()
  {
    swap(*this, other);
    std::cout << "Array of size " << size_ << " is moved\n";
  }

  friend void swap(MovableArray& a, MovableArray& b) throw()
  {
    using std::swap;
    swap(a.data_, b.data_);
    swap(a.size_, b.size_);
  }
  int size() const { return this->size_; }
  int operator[](int i) const { return data_[i]; }
  int& operator[](int i) { return data_[i]; }

private:
  MovableArray& operator=(const MovableArray& rhs); // copy operator
  MovableArray& operator=(MovableArray&& rhs);      // move operator
  // MovableArray& operator=(MovableArray rhs);        // both
  int size_;
  int* data_;
};

void
printSize(MovableArray a)
{
  /* for (int i = 0; i < a.size(); ++i) */
  /*   std::cout << a[i] << "\t"; */
  /* std::cout << std::endl; */

  std::cout << "Size of array is " << a.size() << std::endl;
}

MovableArray
createArray(int size)
{
  auto ret = MovableArray(size);
  return ret;
}

int
main()
{
  MovableArray a{ 20 };
  // copied
  printSize(a);
  // explicitly moved
  printSize(std::move(a));
  // implicitly moved, why not working? TODO
  printSize(MovableArray(30));
  // implicitly moved
  // need to be compile with option 
  //     -fno-elide-constructors
  printSize(createArray(40));
}

The output I got is

Array of size 20 is copied
Size of array is 20
deleted array of size 20
Array of size 20 is moved
Size of array is 20
deleted array of size 20
Size of array is 30
deleted array of size 30
Array of size 40 is moved
deleted array of size 0
Size of array is 40
deleted array of size 40
deleted array of size 0

Aucun commentaire:

Enregistrer un commentaire