README Container Design, Step 4 ------------------------------------------------------------------------------ In this step we have added operations for inserting, inspecting, and removing elements, push_back(), back(), and pop(). Here is the copy version of push_back(): template void Container:: push_back(const T& x) { if (size() == capacity()) { resize(x); // can fail } else { *finish_ = x; // can fail } ++finish_; } resize() can fail, when allocating new memory, copy x to its place, or copying objects from old storage to the storage. template void Container:: resize_(const T& x) { size_type old_size = size(); size_type new_capacity = compute_capacity_(); T* start = allocate_(new_capacity); try { *(start + old_size) = x; std::copy(start_, finish_, start); } catch (...) { deallocate_(start); throw; } delete[] start_; start_ = start; finish_ = start_ + old_size; end_of_storage_ = start_ + new_capacity; } When applying copy semantics it's simple to handle problems, just deallocate the new storage and keep the current state of the container. allocate_() is fail-safe, the assignment to x is handled exception neutral, if copy() fails we handle this is handled in an exception-safe way - resize_() is fail-safe. Note: When applying move semantics it gets much more complicated, since we then have to keep track of how many of the objects have been moved when a failure occurs, to be able to restore the state before starting moving elements. According to standard conventions, operations such as back() and pop_back() shall not throw, for example if the container is empty. template const T& Container:: back() const { auto tmp = finish_; --tmp; return *tmp; } template void Container:: pop_back() noexcept { --finish_; finish_->~T(); } Here we don't do anything that should throw - copying a pointer, decrementing a poiner, returning a reference, invoking a destructor. Seems that we are still robust! -------------------------------------------------------------------------------- What are the requirements on the element type T? ------------------------------------------------ - Default constructor, should be exception-safe. - Non-throwing destructor, to be able to guarantee exception-safety. - Copy constructor, should be exception-safe. - Copy assignment operator, should be exception-safe. - Move constructor and move assignment operator, if available, should not fail. If an operation throws, it must guarantee that the target object is still a valid T. ------------------------------------------------------------------------------