Following on from my two previous posts. * [An alternative vector](http://codereview.stackexchange.com/q/121127/507) * [An Alternative Vector (Copy Assignment Operator)](http://codereview.stackexchange.com/q/121180/507) I have written a detailed blog about how to write a minimal vector like class. This set of articles has been inspired by multiple posts here on http://codereview.stackexchange.com (See [Sources](http://lokiastari.com/blog/2016/02/27/vector/#Sources)). * [Index](http://lokiastari.com/c-plus-plus-by-example/) 1. [Resource Management: Allocation](http://lokiastari.com/blog/2016/02/27/vector/) 2. [Resource Management: Copy and Swap](http://lokiastari.com/blog/2016/02/29/vector-resource-management-ii-copy-assignment/) 3. [Resource Management: Resize](http://lokiastari.com/blog/2016/03/12/vector-resize/) 4. [Resource Management: Simple Optimization](http://lokiastari.com/blog/2016/03/19/vector-simple-optimizations/) 5. [The Other Stuff](http://lokiastari.com/blog/2016/03/20/vector-the-other-stuff/) The final result is below. But now it is my turn for some review to make sure I did not screw up too much. :-) Head: #ifndef THORSANVIL_CONTAINER_VECTOR #define THORSANVIL_CONTAINER_VECTOR #include <type_traits> #include <memory> #include <algorithm> #include <stdexcept> #include <iterator> Types: namespace ThorsAnvil { namespace Container { template<typename T> class Vector { public: using value_type = T; using reference = T&; using const_reference = T const&; using pointer = T*; using const_pointer = T const*; using iterator = T*; using const_iterator = T const*; using difference_type = std::ptrdiff_t; using size_type = std::size_t; private: size_type capacity; size_type length; T* buffer; struct Deleter { void operator()(T* buffer) const { ::operator delete(buffer); } }; Constructors: public: Vector(int capacity = 10) : capacity(capacity) , length(0) , buffer(static_cast<T*>(::operator new(sizeof(T) * capacity))) {} template<typename I> Vector(I begin, I end) : capacity(std::distance(begin, end)) , length(0) , buffer(static_cast<T*>(::operator new(sizeof(T) * capacity))) { for(auto loop = begin;loop != end; ++loop) { pushBackInternal(*loop); } } Vector(std::initializer_list<T> const& list) : Vector(std::begin(list), std::end(list)) {} ~Vector() { // Make sure the buffer is deleted even with exceptions // This will be called to release the pointer at the end // of scope. std::unique_ptr<T, Deleter> deleter(buffer, Deleter()); clearElements<T>(); } Vector(Vector const& copy) : capacity(copy.length) , length(0) , buffer(static_cast<T*>(::operator new(sizeof(T) * capacity))) { try { for(int loop = 0; loop < copy.length; ++loop) { push_back(copy.buffer[loop]); } } catch(...) { clearElements<T>(); ::operator delete(buffer); // Make sure the exceptions continue propagating after // the cleanup has completed. throw; } } Vector& operator=(Vector const& copy) { copyAssign<T>(copy); return *this; } Vector(Vector&& move) noexcept : capacity(0) , length(0) , buffer(nullptr) { move.swap(*this); } Vector& operator=(Vector&& move) noexcept { move.swap(*this); return *this; } void swap(Vector& other) noexcept { using std::swap; swap(capacity, other.capacity); swap(length, other.length); swap(buffer, other.buffer); } Access: reference operator[](size_type index) {return buffer[index];} const_reference operator[](size_type index) const {return buffer[index];} reference at(size_type index) {validateIndex(index);return buffer[index];} const_reference at(size_type index) const {validateIndex(index);return buffer[index];} reference front() {return buffer[0];} const_reference front() const {return buffer[0];} reference back() {return buffer[length - 1];} const_reference back() const {return buffer[length - 1];} Comparison: bool operator!=(Vector const& rhs) const {return !(*this == rhs);} bool operator==(Vector const& rhs) const { return (size() == rhs.size()) ? std::equal(begin(), end(), rhs.begin()) : false; } Iterators: iterator begin() {return buffer;} iterator rbegin() {return std::reverse_iterator<iterator>(end());} const_iterator begin() const {return buffer;} const_iterator rbegin() const {return std::reverse_iterator<iterator>(end());} iterator end() {return buffer + length;} iterator rend() {return std::reverse_iterator<iterator>(begin());} const_iterator end() const {return buffer + length;} const_iterator rend() const {return std::reverse_iterator<iterator>(begin());} const_iterator cbegin() const {return begin();} const_iterator crbegin() const {return rbegin();} const_iterator cend() const {return end();} const_iterator crend() const {return rend();} Non-Mutating Functions: size_type size() const {return length;} bool empty() const {return length == 0;} Mutating Functions: void push_back(T const& value) { resizeIfRequire(); pushBackInternal(value); } void push_back(T&& value) { resizeIfRequire(); moveBackInternal(std::forward<T>(value)); } template<typename... Args> void emplace_back(Args&&... args) { resizeIfRequire(); constructBackInternal(std::forward<T>(args)...); } void pop_back() { --length; buffer[length].~T(); } void reserve(size_type capacityUpperBound) { if (capacityUpperBound > capacity) { reserveCapacity(capacityUpperBound); } } Private: private: void validateIndex(size_type index) { if (index >= length) { throw std::out_of_range("Out of Range"); } } void resizeIfRequire() { if (length == capacity) { size_type newCapacity = capacity * 1.62; reserveCapacity(newCapacity); } } void reserveCapacity(size_type newCapacity) { Vector<T> tmpBuffer(newCapacity); simpleCopy<T>(tmpBuffer); tmpBuffer.swap(*this); } void pushBackInternal(T const& value) { new (buffer + length) T(value); ++length; } void moveBackInternal(T&& value) { new (buffer + length) T(std::forward<T>(value)); ++length; } template<typename... Args> void constructBackInternal(Args&&... args) { new (buffer + length) T(std::forward<Args>(args)...); ++length; } template<typename X> typename std::enable_if<std::is_nothrow_move_constructible<X>::value == false>::type simpleCopy(Vector<T>& dst) { std::for_each(buffer, buffer + length, [&dst](T const& v){dst.pushBackInternal(v);} ); } template<typename X> typename std::enable_if<std::is_nothrow_move_constructible<X>::value == true>::type simpleCopy(Vector<T>& dst) { std::for_each(buffer, buffer + length, [&dst](T& v){dst.moveBackInternal(std::move(v));} ); } template<typename X> typename std::enable_if<std::is_trivially_destructible<X>::value == false>::type clearElements() { // Call the destructor on all the members in reverse order for(int loop = 0; loop < length; ++loop) { // Note we destroy the elements in reverse order. buffer[length - 1 - loop].~T(); } } template<typename X> typename std::enable_if<std::is_trivially_destructible<X>::value == true>::type clearElements() { // Trivially destructible objects can be re-used without using the destructor. } template<typename X> typename std::enable_if<(std::is_nothrow_copy_constructible<X>::value && std::is_nothrow_destructible<X>::value) == true>::type copyAssign(Vector<X>& copy) { if (this == ©) { return; } if (capacity <= copy.length) { clearElements<T>(); length = 0; for(int loop = 0; loop < copy.length; ++loop) { pushBackInternal(copy[loop]); } } else { // Copy and Swap idiom Vector<T> tmp(copy); tmp.swap(*this); } } template<typename X> typename std::enable_if<(std::is_nothrow_copy_constructible<X>::value && std::is_nothrow_destructible<X>::value) == false>::type copyAssign(Vector<X>& copy) { // Copy and Swap idiom Vector<T> tmp(copy); tmp.swap(*this); } }; Tail: } } #endif