
On Apr 15, 2006, at 3:06 PM, Daniel Walker wrote:
On 4/15/06, David Abrahams <dave@boost-consulting.com> wrote:
"Daniel Walker" <daniel.j.walker@gmail.com> writes:
On 4/11/06, David Abrahams <dave@boost-consulting.com> wrote:
Thorsten Ottosen <thorsten.ottosen@dezide.com> writes:
cannot have overloads (in C++98) taking containers/ranges:
fn( Iterator first, Iterator last ) fn( Iterator first, Iterator last, Functor f ) fn( Range rng ) fn( Range rng, Functor f )
as the last overload is ambiguous. Concepts will allow this to be resolved as a Functor will not match the Iterator requirements :).
you can use enable_if on the latter and disable it the two types are the same.
Not if you happen to have a type that is both a valid range and a valid function object.
Yes, that's a corner case, but it's the corner of a large floating block of ice.
For this idiom, I use boost::iterator_range like so,
fn( Iterator first, Iterator last ) fn( Iterator first, Iterator last, Functor f ) fn( boost::iterator_range<Iterator> rng ) fn( boost::iterator_range<Iterator> rng, Functor f )
This is not generic. Now you can't pass other valid Ranges (e.g. std::vector<T>) as the first argument to fn.
Yeah, I know. This is just a work-around because the generic version isn't possible today due to the overload ambiguity. You can indirectly use the functions with other valid Ranges like vector, but it's a hassle because you first have to convert them using make_iterator_range(). In some circumstances, for the time being, this may be acceptable for this particular idiom (overloading algorithms to take both iterators and ranges), but really I agree that it further illustrates the need for in-language support for concepts in a future version of C++ in order to make the overloads generic.
Actually in this particular case, all we need is a more smartly designed std::iterator_traits class: Spec: --- template <class Iterator> struct iterator_traits { }; However if Iterator has the nested types: difference_type, value_type, pointer, reference and iterator_category, and if Iterator::iterator_category (if it exists) is implicitly convertible to either std::input_iterator_tag, or std::output_iterator_tag, then the struct iterator_traits is instead: template <class Iterator> struct iterator_traits { typedef typename Iterator::difference_type difference_type; typedef typename Iterator::value_type value_type; typedef typename Iterator::pointer pointer; typedef typename Iterator::reference reference; typedef typename Iterator::iterator_category iterator_category; }; Plus the pointer specializations... Plus any user-defined specializations... --- Such an iterator_traits is easy to implement today with has_member_* and is_convertible. And now you can say iterator_traits<any type at all>. The instantiation always compiles and may or may not have the nested typedefs. Then you can make an is_input_iterator trait: A type T is an input iterator if iterator_traits<T> has a member called iterator_category that is convertible to std::input_iterator_tag. enable_if your fn with is_input_iterator<InputIterator>, and you're done. You can get most of the way there with today's iterator_traits. You'll miss user-defined iterators that specialize iterator_traits instead of containing the nested types directly (which is practically no types at all). Just change is_iterator to look for the nested types, and then specialize is_iterator for pointer types. Then you can build is_input_iterator on top of is_iterator. That's not an argument against concepts. Its just a fun and useful exercise to build is_input_iterator<T>. Maybe someone can see a better way to do it with today's iterator_traits than what I've described. -Howard