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.
This commit is contained in:
Robert Leahy
2019-12-22 19:22:47 -05:00
parent 27c1fea452
commit d4a0917821
2 changed files with 52 additions and 0 deletions

View File

@@ -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;

View File

@@ -24,6 +24,7 @@
#include <boost/core/lightweight_test.hpp>
#include <boost/static_assert.hpp>
#include <cstring>
#include <iterator>
#include <new>
using namespace boost::container;
@@ -56,6 +57,40 @@ BOOST_INTRUSIVE_INSTANTIATE_DEFAULT_TYPE_TMPLT(const_reverse_iterator)
}}} //namespace boost::container::test {
template<class RandomAccessIterator>
void check_plus_zero_impl(RandomAccessIterator it)
{
RandomAccessIterator cpy(it + 0);
BOOST_TEST(cpy == it);
}
template<class Container, class Category>
void check_plus_zero(const Category&)
{}
template<class Container>
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<class Container>
void check_plus_zero()
{
typedef typename Container::iterator iterator;
typedef typename std::iterator_traits<iterator>::iterator_category category;
category tag;
check_plus_zero<Container>(tag);
}
template<class Container>
void check_null_iterators()
{
@@ -77,20 +112,35 @@ void check_null_iterators()
int main()
{
check_null_iterators< vector<int> >();
check_plus_zero< vector<int> >();
check_null_iterators< deque<int> >();
check_plus_zero< deque<int> >();
check_null_iterators< stable_vector<int> >();
check_plus_zero< stable_vector<int> >();
check_null_iterators< static_vector<int, 1> >();
check_plus_zero< static_vector<int, 1> >();
check_null_iterators< string >();
check_plus_zero< string >();
check_null_iterators< list<int> >();
check_plus_zero< list<int> >();
check_null_iterators< slist<int> >();
check_plus_zero< slist<int> >();
check_null_iterators< map<int, int> >();
check_plus_zero< map<int, int> >();
check_null_iterators< multimap<int, int> >();
check_plus_zero< multimap<int, int> >();
check_null_iterators< set<int> >();
check_plus_zero< set<int> >();
check_null_iterators< multiset<int> >();
check_plus_zero< multiset<int> >();
check_null_iterators< flat_set<int> >();
check_plus_zero< flat_set<int> >();
check_null_iterators< flat_multiset<int> >();
check_plus_zero< flat_multiset<int> >();
check_null_iterators< flat_map<int, int> >();
check_plus_zero< flat_map<int, int> >();
check_null_iterators< flat_multimap<int, int> >();
check_plus_zero< flat_multimap<int, int> >();
return boost::report_errors();
}