From d4a0917821992fd0a1d900c9eca0d903dc7940d2 Mon Sep 17 00:00:00 2001 From: Robert Leahy Date: Sun, 22 Dec 2019 19:22:47 -0500 Subject: [PATCH] RandomAccessIterator + 0 Previously the iterators of boost::container::deque would assert when zero was added to them in at least the following situations: - The iterator was obtained by a call to boost::container::deque::begin and boost::container::deque::empty returns true - The iterator was default constructed This is inconsistent with the way in which the iterators of boost:: container::deque have behaved historically and is also inconsistent with an understanding of iterators developed by analogy with pointers: - Adding zero to a null pointer is valid despite the fact the null pointer cannot be dereferenced - Adding zero to a pointer that is one past the end yields a pointer which is still one past the end and thus which has well defined semantics Fixed this issue and codified the expected behavior in unit tests. --- include/boost/container/deque.hpp | 2 ++ test/null_iterators_test.cpp | 50 +++++++++++++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/include/boost/container/deque.hpp b/include/boost/container/deque.hpp index ae91106..cea31e3 100644 --- a/include/boost/container/deque.hpp +++ b/include/boost/container/deque.hpp @@ -234,6 +234,8 @@ class deque_iterator deque_iterator& operator+=(difference_type n) BOOST_NOEXCEPT_OR_NOTHROW { + if (!n) + return *this; BOOST_ASSERT(!!m_cur); difference_type offset = n + (this->m_cur - this->m_first); const difference_type block_size = this->m_last - this->m_first; diff --git a/test/null_iterators_test.cpp b/test/null_iterators_test.cpp index f081dbe..525d11d 100644 --- a/test/null_iterators_test.cpp +++ b/test/null_iterators_test.cpp @@ -24,6 +24,7 @@ #include #include #include +#include #include using namespace boost::container; @@ -56,6 +57,40 @@ BOOST_INTRUSIVE_INSTANTIATE_DEFAULT_TYPE_TMPLT(const_reverse_iterator) }}} //namespace boost::container::test { +template +void check_plus_zero_impl(RandomAccessIterator it) +{ + RandomAccessIterator cpy(it + 0); + BOOST_TEST(cpy == it); +} + +template +void check_plus_zero(const Category&) +{} + +template +void check_plus_zero(const std::random_access_iterator_tag&) +{ + check_plus_zero_impl(typename Container::iterator()); + check_plus_zero_impl(typename Container::const_iterator()); + check_plus_zero_impl(typename Container::reverse_iterator()); + check_plus_zero_impl(typename Container::const_reverse_iterator()); + Container c; + check_plus_zero_impl(c.begin()); + check_plus_zero_impl(c.cbegin()); + check_plus_zero_impl(c.rbegin()); + check_plus_zero_impl(c.crbegin()); +} + +template +void check_plus_zero() +{ + typedef typename Container::iterator iterator; + typedef typename std::iterator_traits::iterator_category category; + category tag; + check_plus_zero(tag); +} + template void check_null_iterators() { @@ -77,20 +112,35 @@ void check_null_iterators() int main() { check_null_iterators< vector >(); + check_plus_zero< vector >(); check_null_iterators< deque >(); + check_plus_zero< deque >(); check_null_iterators< stable_vector >(); + check_plus_zero< stable_vector >(); check_null_iterators< static_vector >(); + check_plus_zero< static_vector >(); check_null_iterators< string >(); + check_plus_zero< string >(); check_null_iterators< list >(); + check_plus_zero< list >(); check_null_iterators< slist >(); + check_plus_zero< slist >(); check_null_iterators< map >(); + check_plus_zero< map >(); check_null_iterators< multimap >(); + check_plus_zero< multimap >(); check_null_iterators< set >(); + check_plus_zero< set >(); check_null_iterators< multiset >(); + check_plus_zero< multiset >(); check_null_iterators< flat_set >(); + check_plus_zero< flat_set >(); check_null_iterators< flat_multiset >(); + check_plus_zero< flat_multiset >(); check_null_iterators< flat_map >(); + check_plus_zero< flat_map >(); check_null_iterators< flat_multimap >(); + check_plus_zero< flat_multimap >(); return boost::report_errors(); }