#ifndef INCLUDED_TRNX_VECTOR #define INCLUDED_TRNX_VECTOR #include #include #include #include #include #include namespace trnx { template class vector { public: typedef T value_type; typedef std::size_t size_type; typedef std::ptrdiff_t difference_type; typedef T& reference; typedef const T& const_reference; typedef T* pointer; typedef const T* const_pointer; private: size_type d_size; size_type d_capacity; typedef detail::uninitialized buff_type[]; std::unique_ptr d_buff; // `uninitialized` represents enough uninitialized storage for an // instance of `T`. The lifetime of the `T` instance is controlled // manually via the `.construct` and `.destroy` member functions. // An initialized instance can be accessed through `.get`. public: typedef detail::iter_impl > iterator; //CHG typedef detail::iter_impl > const_iterator; //CHG // `iter_impl` is an iterator type that automatically transforms // `uninitialized*` into `T*` when dereferencing. vector() : d_size(0), d_capacity(0), d_buff(nullptr) { // A default-constructed vector is empty and has no allocated buffer. } vector(const vector& rhs) : d_size(rhs.d_size), d_capacity(rhs.d_capacity), // Allocate enough space for `rhs.d_size` items. d_buff(std::make_unique(rhs.d_size)) { // Copy-construct all of `rhs`'s elements into the current buffer. for(size_type i = 0; i < d_size; ++i) { d_buff[i].construct(rhs.d_buff[i].get()); } } ~vector() { // Destroy all elements in the vector. In the Standard, the order of // destruction is unspecified. for(size_type i = 0; i < d_size; ++i) { d_buff[i].destroy(); } } vector& operator=(const vector& rhs); // The copy-constructor allocates enough memory to store `rhs`'s // elements in a new buffer, copies over the current elements to the // new buffer, and finally sets `d_buff` to point to the new buffer. size_type size() const { return d_size; } size_type capacity() const { return d_capacity; } bool empty() const { return d_size == 0; } iterator begin() { return iterator(d_buff.get()); } iterator begin() const { return iterator(d_buff.get()); } iterator end() { return iterator(d_buff.get() + d_size); } iterator end() const { return iterator(d_buff.get() + d_size); } const_iterator cbegin() const { return const_iterator(d_buff.get()); } const_iterator cend() const { return const_iterator(d_buff.get() + d_size); } reference front() { return d_buff[0].get(); } const_reference front() const { return d_buff[0].get(); } reference back() { return d_buff[d_size - 1].get(); } const_reference back() const { return d_buff[d_size - 1].get(); } reference operator[](size_type i) { return d_buff[i].get(); } const_reference operator[](size_type i) const { return d_buff[i].get(); } reference at(size_type i) { if(i >= d_size) { throw std::out_of_range("Index out of range"); } else { return d_buff[i].get(); } } const_reference at(size_type i) const { // The following use of `const_cast` is legal and simply prevents code // repetition with the non-`const` version of `at`. return const_cast&>(*this).at(i); } void push_back(const T& item); void push_back(T&& item); void pop_back(); void clear(); void reserve(size_type new_capacity); }; template void vector::push_back(const T& item) { if(d_size >= d_capacity) { const size_type new_capacity = (d_capacity == 0) ? 1 : d_capacity * 2; reserve(new_capacity); } d_buff[d_size++].construct(item); } template void vector::push_back(T&& item) { if(d_size >= d_capacity) { const size_type new_capacity = (d_capacity == 0) ? 1 : d_capacity * 2; reserve(new_capacity); } d_buff[d_size++].construct(std::move(item)); } template void vector::pop_back() { BSLS_ASSERT(!empty()); d_buff[d_size - 1].destroy(); --d_size; } template void vector::clear() { // Destroy all existing elements for(size_type i = 0; i < d_size; ++i) { d_buff[i].destroy(); } // Set size to zero, but leave capacity unchanged d_size = 0; } template vector& vector::operator=(const vector& rhs) { // Prevent self-assignment if(&rhs != this) { // Destroy all existing elements and set size to zero clear(); // Reserve if necessary reserve(rhs.d_size); // Copy elements from `rhs` for(size_type i = 0; i < rhs.d_size; ++i) { d_buff[i].construct(rhs.d_buff[i].get()); } // Update size d_size = rhs.d_size; } return *this; } template void vector::reserve(size_type new_capacity) { // Exit early if there's no need to reserve more memory if(new_capacity <= d_capacity) { return; } // Allocate a new buffer auto buff = std::make_unique(new_capacity); // Copy-construct existing elements into the new buffer and destroy them // in the old one for(size_type i = 0; i < d_size; ++i) { buff[i].construct(std::move(d_buff[i].get())); d_buff[i].destroy(); } // Deallocate old buffer, set the owned buffer to `buff` d_buff = std::move(buff); // Update capacity d_capacity = new_capacity; } } // close namespace trnx #endif