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: Tony Lewis <tonyelewis@…> 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_DEBUG is 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 ivan.lelann@…, 7 years ago

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

comment:2 by raad@…, 7 years ago

Cc: raad@… added

comment:3 by Andrey Semashev, 7 years ago

Cc: Andrey.Semashev@… added

comment:4 by Andrey Semashev, 5 years ago

Resolution: fixed
Status: newclosed
Note: See TracTickets for help on using tickets.