Boost logo

Boost Users :

Subject: [Boost-users] A forward iterator need not be default-constructible
From: Krzysztof Żelechowski (giecrilj_at_[hidden])
Date: 2011-09-26 13:48:26


== Problem ==

The following code fails to compile:

  #include <vector>
  #include <boost/iterator/transform_iterator.hpp>
  #include <boost/range/algorithm.hpp>
  #include <boost/range/adaptor/transformed.hpp>
  static inline void trigger (::std:: vector < int > const &p_v)
  {
  /* i: p_v [i] + 0 >= 0 */
  ::boost:: lower_bound
  (p_v |
  ::boost:: adaptors:: transformed
  (::std:: bind1st (::std:: plus < int > (), 0)), 0); }

Details: See [[#Evidence]].

== Workaround ==

The following definition causes the code to compile successfully, as of GNU
C++ 4.6:

  #define BOOST_RANGE_ENABLE_CONCEPT_ASSERT 0

Disadvantages and risks:

  * The macro BOOST_RANGE_ENABLE_CONCEPT_ASSERT is undocumented.

  * It disables concept checking altogether, which may be a good thing or a
bad thing, depending on your needs. The official motivation for concept
checking is that it makes error messages more readable; in this particular
case, I am not convinced.

== Background ==

TL;DR: If all you can say is that such an iterator does not meet the
requirements imposed by the standard, I am not interested. If you are
willing not to treat the standard as the ultimate and unquestionable oracle,
you may read on :-)

The standard C++ library defined iterators to serve and additional task to
serve as abstract pointers that may be singular, i.e. not corresponding to
any object and distinct from any other iterator. To this end, the standard
requires that interators may be constructed out of thin air, and that such
an iterator is singular. This requirement is only useful iterators that do
not correspond to containers like the standard input stream iterator which
you can use to find something in it, and even then the end iterator, which
is singular, is created explicitly by the calling code and not by library
code.

Requiring conformance to this particular requirement in Boost is
counterproductive, as it causes perfectly valid and obvious code to fail to
compile for no reason at all except for respecting the letter of the
standard.

== Conclusions ==

  1. The macro BOOST_RANGE_ENABLE_CONCEPT_ASSERT should be documented, as it
is necessary to keep the concepts BS at bay.

  2. The concept mechanism used by Boost should not require singular
iterators to exist; the standard is obnoxious and misguided here and
promotes sloppy coding.

== Note ==

I believe that it is possible to design algorithms so that they do not
construct singular iterators anywhere; moreover, I believe that doing so
would be beneficial to the library implementation, as evidenced in GNU
libstdc++ Bug #45488. (My similar report against Plauger’s library was
declined by Microsoft, which means I have to use a replacement algorithm
fixed by myself.)

== Evidence ==

g++ -g doit.cpp -o doit
In file included from doit.cpp:4:0:
/usr/include/boost/iterator/transform_iterator.hpp: In constructor
‘boost::transform_iterator<UnaryFunction, Iterator, Reference,
Value>::transform_iterator() [with UnaryFunc = std::binder1st<std::plus<int>
>, Iterator = __gnu_cxx::__normal_iterator<const int*, std::vector<int> >,
Reference = boost::use_default, Value = boost::use_default]’:
/usr/include/boost/concept_check.hpp:130:10: instantiated from
‘boost::DefaultConstructible<TT>::~DefaultConstructible() [with TT =
boost::transform_iterator<std::binder1st<std::plus<int> >,
__gnu_cxx::__normal_iterator<const int*, std::vector<int> >,
boost::use_default, boost::use_default>]’
/usr/include/boost/range/concepts.hpp:148:16: instantiated from ‘static
void boost::concepts::requirement<boost::concepts::failed************
Model::************>::failed() [with Model =
boost::range_detail::ForwardIteratorConcept<
boost::transform_iterator<std::binder1st<std::plus<int> >,
__gnu_cxx::__normal_iterator<const int*, std::vector<int> >,
boost::use_default, boost::use_default> >]’
/usr/include/boost/range/concepts.hpp:264:1: instantiated from
‘boost::ForwardRangeConcept<const
boost::range_detail::transform_range<std::binder1st<std::plus<int> >, const
std::vector<int> > >’
/usr/include/boost/concept/detail/has_constraints.hpp:42:5: instantiated
from ‘const bool
boost::concepts::not_satisfied<boost::ForwardRangeConcept<const
boost::range_detail::transform_range<std::binder1st<std::plus<int> >, const
std::vector<int> > > >::value’
/usr/include/boost/concept/detail/has_constraints.hpp:45:31: instantiated
from ‘boost::concepts::not_satisfied<boost::ForwardRangeConcept<const
boost::range_detail::transform_range<std::binder1st<std::plus<int> >, const
std::vector<int> > > >’
/usr/include/boost/mpl/if.hpp:67:11: instantiated from ‘boost::mpl::if_
<boost::concepts::not_satisfied<boost::ForwardRangeConcept<const
boost::range_detail::transform_range<std::binder1st<std::plus<int> >, const
std::vector<int> > > >,
boost::concepts::constraint<boost::ForwardRangeConcept<const
boost::range_detail::transform_range<std::binder1st<std::plus<int> >, const
std::vector<int> > > >,
boost::concepts::requirement<boost::concepts::failed************
boost::ForwardRangeConcept<const
boost::range_detail::transform_range<std::binder1st<std::plus<int> >, const
std::vector<int> > >::************> >’
/usr/include/boost/concept/detail/general.hpp:50:8: instantiated from
‘boost::concepts::requirement_<void (*)(boost::ForwardRangeConcept<const
boost::range_detail::transform_range<std::binder1st<std::plus<int> >, const
std::vector<int> > >)>’
/usr/include/boost/range/algorithm/lower_bound.hpp:45:1: instantiated from
‘typename boost::range_iterator<const ForwardRange>::type
boost::range::lower_bound(const ForwardRange&, Value) [with ForwardRange =
boost::range_detail::transform_range<std::binder1st<std::plus<int> >, const
std::vector<int> >, Value = int, typename boost::range_iterator<const
ForwardRange>::type =
boost::transform_iterator<std::binder1st<std::plus<int> >,
__gnu_cxx::__normal_iterator<const int*, std::vector<int> >,
boost::use_default, boost::use_default>]’
doit.cpp:8:114: instantiated from here
/usr/include/boost/iterator/transform_iterator.hpp:99:26: error: no matching
function for call to ‘std::binder1st<std::plus<int> >::binder1st()’
/usr/include/boost/iterator/transform_iterator.hpp:99:26: note: candidates
are:
/usr/include/c++/4.6/backward/binders.h:109:7: note:
std::binder1st<_Operation>::binder1st(const _Operation&, const typename
_Operation::first_argument_type&) [with _Operation = std::plus<int>,
typename _Operation::first_argument_type = int]
/usr/include/c++/4.6/backward/binders.h:109:7: note: candidate expects 2
arguments, 0 provided
/usr/include/c++/4.6/backward/binders.h:100:11: note:
std::binder1st<std::plus<int> >::binder1st(const
std::binder1st<std::plus<int> >&)
/usr/include/c++/4.6/backward/binders.h:100:11: note: candidate expects 1
argument, 0 provided
Kompilacja nie powiodła się
make: *** [doit] BÅ‚Ä…d 1


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net