#include <functional>
#include <memory>
#include <thread>
#include <future>
#include <iostream>
#include <type_traits>
#include <vector>
#include <boost/bind.hpp>
#include <boost/optional.hpp>

namespace experiment {

/**
 * Array with c++11 move semantics for research purposes.
 *
 * @note Do not use this in real code.
 */
class Array {
  public:
    Array() : arr_{nullptr}, size_{0} {
      std::cout << "default construct " << to_s() << "\n";
    }

    Array(std::initializer_list<int> l)
      : arr_(new int[l.size()]{})
      , size_(l.size()) {

      std::copy(l.begin(), l.end(), arr_);
      std::cout << "create list " << to_s() << "\n";
    }

    Array(const std::vector<int>& src) {
      arr_ = new int[src.size()]{};
      size_ = src.size();
      std::copy(src.begin(), src.end(), arr_);
      std::cout << "create " << to_s() << "\n";
    }

    Array(const Array& other) : arr_(new int[other.size_]{}), size_{other.size_} {
      std::copy(other.arr_, other.arr_ + other.size_, arr_);
      std::cout << "copy construct " << to_s() << "\n";
    }

    Array& operator=(const Array& other) {
      Array tmp{other};
      swap(*this, tmp);
      std::cout << "copy assign " << to_s() << "\n";
      return *this;
    }

    Array(Array&& other) noexcept : arr_{other.arr_}, size_(other.size_) {
      other.arr_ = nullptr;
      other.size_ = 0;
      std::cout << "move construct " << to_s() << "\n";
    }

    Array& operator=(Array&& other) noexcept {
      swap(*this, other);
      std::cout << "move assign " << to_s() << "\n";
      return *this;
    }

    ~Array() {
      std::cout << "destroy " << to_s() << "\n";
      if (arr_) {
        delete [] arr_;
      }
    }

    void reset() {
      if (arr_) {
        delete [] arr_;
        arr_ = nullptr;
      }
      size_ = 0;
    }

    std::string to_s() const {
      std::string ret = std::to_string(intptr_t(this)) + " ";
      if (!arr_) {
        return ret;
      }

      for (int i = 0; i < size_; i++) {
        ret += std::to_string(arr_[i]) + " ";
      }

      return ret;
    }

    friend void swap(Array& lhs, Array& rhs) noexcept;

  private:
    int* arr_;
    int size_;
};

void swap(Array& lhs, Array& rhs) noexcept {
  using std::swap;
  swap(lhs.arr_, rhs.arr_);
  swap(lhs.size_, rhs.size_);
}
}

using namespace experiment;

int main(int , char* []) {
}