How to determine the type of is_iterator type?

  • I am trying to code a trait like is_iterator<T> . Where, when T is the iterator type is_iterator<T>::value == true , otherwise - is_iterator<T>::value == false .

  • What I have tried so far:


 template <class, class Enable = void> struct is_iterator : std::false_type {}; template <typename T> struct is_iterator<T, typename std::enable_if<std::is_pointer<typename std::iterator_traits<T>::pointer>::value>::type> : std::true_type {}; 

Live demo


Q: Is there a more suitable way to define a trait like is_iterator than the one shown above?

+7
c ++ iterator c ++ 11 typetraits c ++ 14
source share
3 answers

As I said in the comments, the solutions presented here are based on the non-portable properties of iterators_traits in some implementations. According to the C ++ 03 and C ++ 11 standards, iterator_traits is defined only for iterators (and the special case of pointers), so any other use is undefined behavior. In particular, the use of iterator_traits<T>::pointer in the SFINAE context is not guaranteed because the iterator_traits<T> instance will refer to T::value_type , T::pointer , T::iterator_category , etc., and this happens Beyond the β€œimmediate context,” where SFINAE doesn't apply.

C ++ 14 will establish that it should have fixed it (this happened after C ++ 14 with DR 2408 ), but for C ++ 11, a safe way to determine is_iterator is to write a is_iterator that checks all the necessary operations that the iterator must determine. The only operations that all iterators should support are operator* and pre- and post-increment. Unfortunately, there may be types that define those operations that are not valid iterators, so writing the correct attribute is quite difficult.

+5
source share

Verification error if std::iterator_traits<T>::pointer is a type that is not a pointer, for example, if T = std::ostream_iterator<U> .

I think the best test could be whether std::iterator_traits<T>::iterator_category either std::input_iterator_tag , or a derived type, or std::output_iterator_tag .

 template <class, class Enable = void> struct is_iterator : std::false_type {}; template <typename T> struct is_iterator <T, typename std::enable_if< std::is_base_of<std::input_iterator_tag, typename std::iterator_traits<T>::iterator_category>::value || std::is_same<std::output_iterator_tag, typename std::iterator_traits<T>::iterator_category>::value >::type> : std::true_type {}; 
+3
source share

I think there is no need to check for any specific iterator_traits property of nested typedefs. That should be enough to check for iterator_traits<T>::value_type (or any other nested typedef, for that matter), because each iterator has one.

 #include <type_traits> #include <iostream> #include <iterator> template<typename> struct void_ { typedef void type; }; // remove typename spam below: template<typename Discard> using void_t=typename void_<Discard>::type; template<typename T> using decay_t=typename std::decay<T>::type; // stick helper types into details, so the interface // for is_iterator is cleaner: namespace details { template<typename T, typename Enable=void> sturct is_iterator : is_iterator2<T, Enable> {}; // special case: void* is not an iterator // but T* specialization would pick it up // if there weren't the following: template<typename V> struct is_iterator<V*, decay_t<V>> : std::false_type {}; // phase 2: SFINAE pass to std::iterator_traits test // valid in C++14, and in many C++11 compilers, except // for above void issue: template<typename, typename Enable = void> struct is_iterator2 : std::false_type {}; template<typename T> struct is_iterator2<T, void_t< typename std::iterator_traits<T>::value_type> > : std::true_type {}; } template<typename T> struct is_iterator : details::is_iterator<T> {}; int main() { std::cout << is_iterator<int*>::value << is_iterator<double>::value; } 

Unfortunately, this is not guaranteed to work in C ++ 11, but C ++ 14 will fix it (thanks to Jonathan Wackel , specifying it).

+3
source share

All Articles