
"Alex Chovanec" <achovane@engin.umich.edu> writes:
"David Abrahams" <dave@boost-consulting.com> wrote in message news:uacwmy7tw.fsf@boost-consulting.com...
"Alex Chovanec" <achovane@engin.umich.edu> 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.
No it isn't. A valid iterator may have a specialized std::iterator_traits. We can't change std::iterator_traits.
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?
Believe me, I've been over and over this one. There's no solution in the current language. Another one is is_assignable. -- Dave Abrahams Boost Consulting http://www.boost-consulting.com