1
       2
       3
       4
       5
       6
       7
       8
       9
      10
      11
      12
      13
      14
      15
      16
      17
      18
      19
      20
      21
      22
      23
      24
      25
      26
      27
      28
      29
      30
      31
      32
      33
      34
      35
      36
      37
      38
      39
      40
      41
      42
      43
      44
      45
      46
      47
      48
      49
      50
      51
      52
      53
      54
      55
      56
      57
      58
      59
      60
      61
      62
      63
      64
      65
      66
      67
      68
      69
      70
      71
      72
      73
      74
      75
      76
      77
      78
      79
      80
      81
      82
      83
      84
      85
      86
      87
      88
      89
      90
      91
      92
      93
      94
      95
      96
      97
      98
      99
     100
     101
     102
     103
     104
     105
     106
     107
     108
     109
     110
     111
     112
     113
     114
     115
     116
     117
#ifndef ARRAY_HPP
#define ARRAY_HPP

#include <algorithm>
#include <cassert>
#include <cstdlib>
#include <exception>
#include <initializer_list>
#include <iterator>
#include <new>
#include <sstream>
#include <string>
#include <utility>

template<typename T>
class Array {
   private:
      using Tag = enum {AllocateStorage};
      Array(Tag tag, std::size_t size) :
	 size(size),
	 data(size > 0?
	       static_cast<T*>(operator new(sizeof(T) * size))
	    :
	       nullptr) {
      }
   public:
      class Exception: public std::exception {};
      class OutOfRange: public Exception {
	 public:
	    OutOfRange(std::size_t index, std::size_t size) {
	       std::ostringstream os;
	       os << "index " << index << " is out of range [0, " <<
		  size << ")";
	       message = os.str();
	    }
	    const char* what() const noexcept override {
	       return message.c_str();
	    }
	 private:
	    std::string message;
      };
      Array() noexcept : size(0), data(nullptr) {
      }
      Array(std::size_t size) : Array(AllocateStorage, size) {
	 for (std::size_t index = 0; index < size; ++index) {
	    new (data + index) T();
	 }
      }
      Array(std::size_t size, const T& t) : Array(AllocateStorage, size) {
	 for (std::size_t index = 0; index < size; ++index) {
	    new (data + index) T(t);
	 }
      }
      template<typename Iterator>
      Array(Iterator it1, Iterator it2) :
	    Array(AllocateStorage, std::distance(it1, it2)) {
	 std::size_t index = 0;
	 while (it1 != it2 && index < size) {
	    new (data + index++) T(*it1++);
	 }
	 size = index; // just in case
      }
      Array(std::initializer_list<T> elements) :
	    Array(elements.begin(), elements.end()) {
      }
      Array(const Array& other) : Array(AllocateStorage, other.size) {
	 for (std::size_t index = 0; index < size; ++index) {
	    new (data + index) T(other.data[index]);
	 }
      }
      friend void swap(Array& a1, Array& a2) noexcept {
	 using std::swap;
	 swap(a1.size, a2.size);
	 swap(a1.data, a2.data);
      }
      Array(Array&& other) noexcept : Array() {
	 swap(*this, other);
      }
      ~Array() {
	 for (std::size_t index = 0; index < size; ++index) {
	    data[index].~T();
	 }
	 operator delete(data);
      }
      Array& operator=(Array other) noexcept {
	 swap(*this, other);
	 return *this;
      }
      std::size_t get_size() const noexcept {
	 return size;
      }
      T& operator()(std::size_t index) {
	 if (index >= size) throw OutOfRange(index, size);
	 return data[index];
      }
      const T& operator()(std::size_t index) const {
	 if (index >= size) throw OutOfRange(index, size);
	 return data[index];
      }
      T* begin() noexcept {
	 return data;
      }
      const T* begin() const noexcept {
	 return data;
      }
      T* end() noexcept {
	 return data + size;
      }
      const T* end() const noexcept {
	 return data + size;
      }
   private:
      std::size_t size;
      T* data;
};

#endif