#include <iostream>
#include <algorithm>
#include <chrono>
#include <stdlib.h>
#include <time.h> 

struct Copyable
{
  static const int SIZE = 10000;

  Copyable()
  {
    data = new int[SIZE];
    for(int i = 0; i < sizeof(data); ++i)
      data[i] = rand();
  }
  virtual ~Copyable()
  {
    delete[] data;
  }

  Copyable(const Copyable& other)
  {
    data = new int[SIZE];
    std::copy(other.data, other.data + SIZE, data);
  }
  Copyable& operator=(const Copyable& other)
  {
    if(this == &other) return *this;
    delete[] data;
    data = new int[SIZE];
    std::copy(other.data, other.data + SIZE, data);
    return *this;
  }
protected:
  int *data;
};

struct Movable
{
  static const int SIZE = 10000;

  Movable()
  {
    data = new int[SIZE];
    for(int i = 0; i < sizeof(data); ++i)
      data[i] = rand();
  }
  virtual ~Movable()
  {
    delete[] data;
  }

  Movable(const Movable& other)
  {
    data = new int[SIZE];
    std::copy(other.data, other.data + SIZE, data);
  }
  Movable& operator=(const Movable& other)
  {
    if(this == &other) return *this;
    delete[] data;
    data = new int[SIZE];
    std::copy(other.data, other.data + SIZE, data);
    return *this;
  }

  Movable(Movable&& other)
  {
    data = other.data;
    other.data = 0;
  }
  Movable& operator=(Movable&& other)
  {
    if(this == &other) return *this;
    int *p = data;
    data = other.data;
    other.data = p;
    return *this;
  }
protected:
  int *data;
};

template <class T>
T generate()
{
  return T();
}

template <class T>
void swap_copy(T& a, T& b)
{
  T t(a);
  a = b;
  b = t;
}

template <class T>
void swap_move(T& a, T& b)
{
  T t(std::move(a));
  a = std::move(b);
  b = std::move(t);
}

template <class Function>
void measure(Function fun, const std::string& msg)
{
  std::chrono::high_resolution_clock::time_point start, end;
  start = std::chrono::high_resolution_clock::now();

  fun();

  end = std::chrono::high_resolution_clock::now();
  auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(end - start).count();
  std::cout << msg << ": " << duration << "ms" << std::endl;
}

void test_copy_return()
{
  Copyable copy_obj;
  for(int i = 0; i < 100000; ++i)
    copy_obj = generate<Copyable>();
}

void test_move_return()
{
  Movable move_obj;
  for(int i = 0; i < 100000; ++i)
    move_obj = generate<Movable>();
}

void test_copy_swap()
{
  Copyable copy_obj_a, copy_obj_b;
  for(int i = 0; i < 100000; ++i)
    swap_copy(copy_obj_a, copy_obj_b);
}

void test_move_swap()
{
  Movable move_obj_a, move_obj_b;
  for(int i = 0; i < 100000; ++i)
    swap_move(move_obj_a, move_obj_b);
}

int main()
{
  srand (time(NULL));

  measure(test_copy_return, "Copy return");
  measure(test_move_return, "Move return");
  measure(test_copy_swap,   "Copy swap");
  measure(test_move_swap,   "Move swap");

  return 0;
}

// > g++ move_rvo_test.cpp -std=c++11 -o prog.exe
// > prog.exe
// Copy return: 224ms
// Move return: 25ms
// Copy swap: 524ms
// Move swap: 2ms

// > g++ move_rvo_test.cpp -std=c++11 -o prog.exe -fno-elide-constructors
// > prog.exe
// Copy return: 500ms
// Move return: 33ms
// Copy swap: 522ms
// Move swap: 1ms