$include_dir="/home/hyper-archives/boost/include"; include("$include_dir/msg-header.inc") ?>
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