How to check if a template parameter is an iterator type or not?

后端 未结 5 1419
迷失自我
迷失自我 2020-12-17 19:21
template
struct is_iterator
{
    static const bool value = ??? // What to write ???
};

int main()
{
    assert(false == is_iterator::valu         


        
相关标签:
5条回答
  • 2020-12-17 19:54

    The original poster clarified that they are actually asking for a way to identify an InputIterator (see http://en.cppreference.com/w/cpp/concept/InputIterator) because they want to be able to increment and dereference the iterator. This has a very simple SFINAE solution in standard C++11, e.g. similar to that from the gcc STL:

    template<typename InputIterator>
    using RequireInputIterator = typename
        std::enable_if<std::is_convertible<typename
                                           std::iterator_traits<InputIterator>::iterator_category,
                                           std::input_iterator_tag>::value>::type;
    
    ...
    
    // Example: declare a vector constructor from a pair of input iterators.
    template <typename InputIterator, typename = RequireInputIterator<InputIterator> >
        MyVector(InputIterator first, InputIterator last) { /* ... */ };
    

    This relies on the iterator type traits classes, which define the typedefs that Armen Tsirunyan thought were required of the iterators themselves. (The iterators can provide those typedefs, but they can also provide them in traits classes, which is necessary in order to use naked pointers as iterators, and the standard library implementations are required to do so.)

    0 讨论(0)
  • 2020-12-17 19:55
    template<class T>
    struct is_iterator
    {   
        static T makeT();
        typedef void * twoptrs[2];  // sizeof(twoptrs) > sizeof(void *)
        static twoptrs & test(...); // Common case
        template<class R> static typename R::iterator_category * test(R); // Iterator
        template<class R> static void * test(R *); // Pointer
    
        static const bool value = sizeof(test(makeT())) == sizeof(void *); 
    };
    
    0 讨论(0)
  • 2020-12-17 19:59
    template < class T, class Enabler = void >
    struct is_iterator : public boost::false_type { };
    
    template < class T >
    struct is_iterator< T, typename boost::enable_if_c<
            sizeof(*(*(T*)0)) + sizeof((*(T*)0)++) + sizeof(++(*(T*)0)) +
            sizeof((*(T*)0) == (*(T*)0)) + sizeof((*(T*)0) != (*(T*)0)) +
            sizeof((*(T*)0) = (*(T*)0)) >::type > : public boost::true_type { };
    
    0 讨论(0)
  • 2020-12-17 20:03

    Well, you could check for the type to have a nested typedef called iterator_category This can be done using SFINAE, and the exact technique can be found in wiki page for SFINAE. This isn't a 100% method, but all decent iterators should provide the common typedefs for iterators, and the iterator_category is one that is unique to iterators. Also don't forget to check if TYPE is simply a pointer. Pointers are iterators.

    0 讨论(0)
  • 2020-12-17 20:11

    Coming in here a few years later, where C++11 and C++14 make it a lot easier to do such things. An iterator is, at its core, something that is dereferencable, incrementable. If it's an input iterator, then also comparable. Let's go with the latter - since that looks like what you want.

    The simplest version would be to use void_t:

    template <typename... >
    using void_t = void;
    

    Base case:

    template <typename T, typename = void>
    struct is_input_iterator : std::false_type { };
    

    Valid case specialization:

    template <typename T>
    struct is_input_iterator<T,
        void_t<decltype(++std::declval<T&>()),                       // incrementable,
               decltype(*std::declval<T&>()),                        // dereferencable,
               decltype(std::declval<T&>() == std::declval<T&>())>>  // comparable
        : std::true_type { };
    

    Alias:

    template <typename T>
    using is_input_iterator_t = typename is_input_iterator<T>::type;
    

    No need to rely on iterator_category or using the tedious C++03 style of check things using overload resolution. Expression SFINAE is where it's at.


    As Mr. Wakely points out in the comments, [iterator.traits] requires that:

    it is required that if Iterator is the type of an iterator, the types

    iterator_traits<Iterator>::difference_type
    iterator_traits<Iterator>::value_type
    iterator_traits<Iterator>::iterator_category
    

    be defined as the iterator’s difference type, value type and iterator category, respectively.

    So we can define our iterator trait to simply check for that:

    template <class T, class = void>
    struct is_iterator : std::false_type { };
    
    template <class T>
    struct is_iterator<T, void_t<
        typename std::iterator_traits<T>::iterator_category
    >> : std::true_type { };
    

    If iterator_traits<T>::iterator_category is ill-formed, then T is not an iterator.

    0 讨论(0)
提交回复
热议问题