Opened 8 years ago
Closed 5 years ago
#10847 closed Bugs (fixed)
Offset overloads of next() / prior() have stopped compiling for iterators that previously worked
| Reported by: | Owned by: | No-Maintainer | |
|---|---|---|---|
| Milestone: | Boost 1.58.0 | Component: | utility |
| Version: | Boost 1.57.0 | Severity: | Regression |
| Keywords: | next, prior, advance, iterator, category, type_traits, has_plus_assign, forward_iterator | Cc: | raad@…, Andrey.Semashev@… |
Description
Summary
In Boost v1.57.0, the offset overloads of boost::next() / boost::prior() have stopped compiling for iterators that previously worked in <= v1.56.0.
I think this is because commit 651a869d4f9479dd3dfdd0293305a60b8c8d0e1c (Boost Trac won't let me paste link without passing some non-existent captcha) has replaced a call to std::advance() with a custom approach using type_traits (such as has_plus_assign).
Unfortunately some iterators indicate their non-provision of random-access through their iterator category (eg forward_iterator_tag) but provide operator+= and operator+ methods/functions that don't compile.
Examples:
- iterators returned by boost::ptr_set and
- iterators returned by GCC's std::list<> when
_GLIBCXX_DEBUGis defined
Importantly, these iterators work without problem under:
- std::advance(),
- Boost v1.56.0's
boost::next()/boost::prior()and - C++11's
std::next()/std::prev()(at least under GCC v4.9.1's implementation)
...so Boost v1.57.0 next() / prior() are out of step with the standard (or std::) approach.
Example
Attempting to compile:
#include <boost/next_prior.hpp>
#include <boost/ptr_container/ptr_set.hpp>
#include <iostream>
#include <list>
int main() {
boost::ptr_set<int> the_ptr_set;
the_ptr_set.insert( new int( 1 ) );
the_ptr_set.insert( new int( 2 ) );
std::cerr << *boost::next( the_ptr_set.begin(), 1 ) << std::endl;
std::list<int> the_list;
the_list.push_back( 1 );
the_list.push_back( 2 );
std::cerr << *boost::next( the_list.begin(), 1 ) << std::endl;
return 0;
}
...with g++ -D_GLIBCXX_DEBUG -o next_prior_prob next_prior_prob.cpp (GCC v4.9.1) produces the following compiler errors:
In file included from /usr/include/c++/4.9/debug/set.h:33:0,
from /usr/include/c++/4.9/debug/set:33,
from /usr/include/c++/4.9/set:66,
from /usr/include/boost/ptr_container/ptr_set.hpp:21,
from next_prior_prob.cpp:2:
/usr/include/c++/4.9/debug/safe_iterator.h: In instantiation of ‘__gnu_debug::_Safe_iterator<_Iterator, _Sequence>& __gnu_debug::_Safe_iterator<_Iterator, _Sequence>::operator+=(const difference_type&) [with _Iterator = std::__cxx1998::_List_iterator<int>; _Sequence = std::__debug::list<int>; __gnu_debug::_Safe_iterator<_Iterator, _Sequence>::difference_type = int]’:
/usr/include/boost/next_prior.hpp:73:11: required from ‘static T boost::next_prior_detail::next_impl1<T, Distance, true>::call(T, Distance) [with T = __gnu_debug::_Safe_iterator<std::__cxx1998::_List_iterator<int>, std::__debug::list<int> >; Distance = int]’
/usr/include/boost/next_prior.hpp:151:67: required from ‘T boost::next(T, Distance) [with T = __gnu_debug::_Safe_iterator<std::__cxx1998::_List_iterator<int>, std::__debug::list<int> >; Distance = int]’
next_prior_prob.cpp:16:49: required from here
/usr/include/c++/4.9/debug/safe_iterator.h:357:13: error: no match for ‘operator+=’ (operand types are ‘std::__cxx1998::_List_iterator<int>’ and ‘const difference_type {aka const int}’)
_M_current += __n;
^
/usr/include/c++/4.9/debug/safe_iterator.h: In instantiation of ‘__gnu_debug::_Safe_iterator<_Iterator, _Sequence>& __gnu_debug::_Safe_iterator<_Iterator, _Sequence>::operator+=(const difference_type&) [with _Iterator = std::_Rb_tree_const_iterator<void*>; _Sequence = std::__debug::set<void*, boost::void_ptr_indirect_fun<std::less<int>, int, int>, std::allocator<void*> >; __gnu_debug::_Safe_iterator<_Iterator, _Sequence>::difference_type = int]’:
/usr/include/boost/ptr_container/detail/void_ptr_iterator.hpp:104:23: required from ‘boost::void_ptr_iterator<VoidIter, T>& boost::void_ptr_iterator<VoidIter, T>::operator+=(boost::void_ptr_iterator<VoidIter, T>::difference_type) [with VoidIter = __gnu_debug::_Safe_iterator<std::_Rb_tree_const_iterator<void*>, std::__debug::set<void*, boost::void_ptr_indirect_fun<std::less<int>, int, int>, std::allocator<void*> > >; T = int; boost::void_ptr_iterator<VoidIter, T>::difference_type = int]’
/usr/include/boost/next_prior.hpp:73:11: required from ‘static T boost::next_prior_detail::next_impl1<T, Distance, true>::call(T, Distance) [with T = boost::void_ptr_iterator<__gnu_debug::_Safe_iterator<std::_Rb_tree_const_iterator<void*>, std::__debug::set<void*, boost::void_ptr_indirect_fun<std::less<int>, int, int>, std::allocator<void*> > >, int>; Distance = int]’
/usr/include/boost/next_prior.hpp:151:67: required from ‘T boost::next(T, Distance) [with T = boost::void_ptr_iterator<__gnu_debug::_Safe_iterator<std::_Rb_tree_const_iterator<void*>, std::__debug::set<void*, boost::void_ptr_indirect_fun<std::less<int>, int, int>, std::allocator<void*> > >, int>; Distance = int]’
next_prior_prob.cpp:11:52: required from here
/usr/include/c++/4.9/debug/safe_iterator.h:357:13: error: no match for ‘operator+=’ (operand types are ‘std::_Rb_tree_const_iterator<void*>’ and ‘const difference_type {aka const int}’)
(but has no problems if boost::next() is replaced with std::next() and -std=c++11 is used)
Change History (4)
comment:1 by , 7 years ago
comment:2 by , 7 years ago
| Cc: | added |
|---|
comment:3 by , 7 years ago
| Cc: | added |
|---|
comment:4 by , 5 years ago
| Resolution: | → fixed |
|---|---|
| Status: | new → closed |

For the same reason, boost::next is broken with std::reverse_iterator (tested with GCC libstdc++ 5.1)
#include <list> #include <vector> #include <iostream> #include <boost/utility.hpp> int main() { std::list<int> ints {1,2,3,4,5,6,7}; // begin/end OK with boost::next or std::next std::cout << *(std::next(begin(ints), 2)) << std::endl; std::cout << *(std::next(end(ints), -2)) << std::endl; std::cout << *(boost::next(begin(ints), 2)) << std::endl; std::cout << *(boost::next(end(ints), -2)) << std::endl; // rbegin/rend KO with boost::next std::cout << *(std::next(rbegin(ints), 2)) << std::endl; std::cout << *(std::next(rend(ints), -2)) << std::endl; // KO std::cout << *(boost::next(rbegin(ints), 2)) << std::endl; std::cout << *(boost::next(rend(ints), -2)) << std::endl; }