[range] Add support for invalidating resize() and workarounds for MSVC and MinGW.

Using of the iterator returned by erase() caused assertion failures (false positive) on MSVC with iterator debugging enabled.
MSVC std::vector can't store non-movable objects if rvalue references are supported.
MinGW std::vector can't store pointers.
This commit is contained in:
Adam Wulkiewicz 2014-06-22 15:17:49 +02:00
parent 81ea76fdb9
commit ba0906369e
2 changed files with 38 additions and 10 deletions

View File

@ -221,6 +221,9 @@ erase(Range & rng,
BOOST_ASSERT(!boost::empty(rng));
BOOST_ASSERT(it != boost::end(rng));
typename boost::range_difference<Range>::type const
d = std::distance(boost::begin(rng), it);
typename boost::range_iterator<Range>::type
next = it;
++next;
@ -228,8 +231,13 @@ erase(Range & rng,
detail::copy_or_move(next, boost::end(rng), it);
range::resize(rng, boost::size(rng) - 1);
// NOTE: assuming that resize() doesn't invalidate the iterators
return it;
// NOTE: In general this should be sufficient:
// return it;
// But in MSVC using the returned iterator causes
// assertion failures when iterator debugging is enabled
// Furthermore the code below should work in the case if resize()
// invalidates iterators when the container is resized down.
return boost::begin(rng) + d;
}
/*!
@ -262,10 +270,11 @@ erase(Range & rng,
typename boost::range_iterator<Range>::type first,
typename boost::range_iterator<Range>::type last)
{
typename std::iterator_traits
<
typename boost::range_iterator<Range>::type
>::difference_type const diff = std::distance(first, last);
typename boost::range_difference<Range>::type const
d = std::distance(boost::begin(rng), first);
typename boost::range_difference<Range>::type const
diff = std::distance(first, last);
BOOST_ASSERT(diff >= 0);
std::size_t const count = static_cast<std::size_t>(diff);
@ -275,9 +284,16 @@ erase(Range & rng,
{
detail::copy_or_move(last, boost::end(rng), first);
range::resize(rng, boost::size(rng) - count);
// NOTE: In general this should be sufficient:
// return first;
// But in MSVC using the returned iterator causes
// assertion failures when iterator debugging is enabled
// Furthermore the code below should work in the case if resize()
// invalidates iterators when the container is resized down.
return boost::begin(rng) + d;
}
// NOTE: assuming that resize() doesn't invalidate the iterators
return first;
}

View File

@ -172,29 +172,41 @@ void test_all()
void test_detail()
{
int arr[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
int arr[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
bgr::detail::copy_or_move(arr + 1, arr + 10, arr);
BOOST_CHECK(arr[0] == 1);
std::vector<int> v(10, 0);
bgr::detail::copy_or_move(v.begin() + 1, v.begin() + 10, v.begin());
BOOST_CHECK(boost::size(v) == 10);
bgr::erase(v, v.begin() + 1);
BOOST_CHECK(boost::size(v) == 9);
int * arr2[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
bgt::NonMovable * arr2[10] = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
bgt::NonMovable foo;
arr2[1] = &foo;
bgr::detail::copy_or_move(arr2 + 1, arr2 + 10, arr2);
std::vector<int*> v2(10, 0);
BOOST_CHECK(arr2[0] == &foo);
// Storing pointers in a std::vector is not possible in MinGW C++98
#if __cplusplus >= 201103L
std::vector<bgt::NonMovable*> v2(10, 0);
bgr::detail::copy_or_move(v2.begin() + 1, v2.begin() + 10, v2.begin());
BOOST_CHECK(boost::size(v2) == 10);
bgr::erase(v2, v2.begin() + 1);
BOOST_CHECK(boost::size(v2) == 9);
#endif
}
int test_main(int, char* [])
{
test_all<int, true>();
test_all<int, false>();
// Storing non-movable elements in a std::vector is not possible in MSVC
#ifndef _MSC_VER
test_all<bgt::NonMovable, true>();
test_all<bgt::NonMovable, false>();
#endif
test_all<bgt::CopyableAndMovable, true>();
test_all<bgt::CopyableAndMovable, false>();