Boost logo

Boost :

From: Alex Chovanec (achovane_at_[hidden])
Date: 2004-08-23 23:17:12


"David Abrahams" <dave_at_[hidden]> wrote in message
news:uacwmy7tw.fsf_at_boost-consulting.com...
> "Alex Chovanec" <achovane_at_[hidden]> writes:
>
> >
> > So 'is_iterator<int *>::value' evaluates to true without generating a
> > compile error, as it should.
>
> *Any* iterator can be implemented the same way. An iterator class
> does not have to have a value_type member!

Ok, you bring up a valid point. I would like to suggest a possible solution
to this problem, but the explanation is a bit long, so please bear with me.
:-)

Originally, I was using the following SFINAE approach in 'is_iterator' to
detect member iterator traits:

    template <typename T>
    static char test(
        T *,
        type_tag<typename T::value_type> * = 0,
        type_tag<typename T::reference> * = 0,
        type_tag<typename T::pointer> * = 0,
        type_tag<typename T::difference_type> * = 0,
        type_tag<typename T::iterator_category> * = 0
    );
    static char (& test(...))[2];

I had considered using Boost iterator traits to do the same thing, like
this:

    template <typename T>
    static char test(
        T *,
        type_tag<typename detail::iterator_traits<T>::value_type> * = 0,
        type_tag<typename detail::iterator_traits<T>::reference> * = 0,
        type_tag<typename detail::iterator_traits<T>::pointer> * = 0,
        type_tag<typename detail::iterator_traits<T>::difference_type> * =
0,
        type_tag<typename detail::iterator_traits<T>::iterator_category> * =
0
    );
    static char (& test(...))[2];

But I felt that the first approach was clearer, and I reasoned that it would
be ok to rely on member traits if I also defined a partial template
specialization to handle pointers.

It wasn't until I read your last email that I tried the second approach and
realized that it doesn't work. It's no surprise that attempting to
instantiate 'iterator_traits<T>' causes a compile error when 'T' is not an
iterator, but I had assumed that SFINAE would simply remove this overload of
'test' from the set of candidate functions. I see now that SFINAE doesn't
work unless you have a valid type on the lefthand side of the '::' operator.

So the problem is not that 'iterator_traits<T>::value_type' does not exist;
the problem is that 'iterator_traits<T>' cannot be instantiated. But what if
'iterator_traits<T>' were defined in such a way that it can still be
instantiated even if 'T' is not an iterator? If 'T' is not an iterator, then
'iterator_traits<T>' can simply be defined to be without any member types.
This is easy to do with a little template metaprogramming. The modified
primary template for Boost iterator traits might look something like this:

    template <typename T>
    struct iterator_traits : public
        mpl::if_<
            mpl::or_<
                has_member_iterator_traits<T>,
                is_pointer<T> >,
            std::iterator_traits<T>,
            empty_iterator_traits
>::type
    {};

Here, 'has_member_iterator_traits' implements my first approach for
detecting member iterator traits, and 'empty_iterator_traits' is just an
empty type. This definition works for both iterators that have member traits
and iterators that are pointers; and it can be specialized for other
iterators just as easily as the existing 'iterator_traits'.

Now, 'is_iterator' can be defined in terms of Boost iterator traits using
the second approach described above. Since 'iterator_traits<T>' can always
be instantiated, SFINAE will take care of the rest, and we won't be bothered
with unwanted compile errors.

What are your thoughts on this approach?

Thanks,

Alex Chovanec


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk