== 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