Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

template<class T>
struct is_iterator
{
    static const bool value = ??? // What to write ???
};

int main()
{
    assert(false == is_iterator<int>::value);
    assert(true == is_iterator<vector<int>::iterator>::value);
    assert(true == is_iterator<list<int>::iterator>::value);
    assert(true == is_iterator<string::iterator>::value);
    assert(true == is_iterator<char*>::value); // a raw pointer is also an iterator
}

The question is: How to make the five assert statements pass?

like image 670
xmllmx Avatar asked Dec 02 '10 14:12

xmllmx


People also ask

How do I find my iterator type?

Iterator type can be checked by using typeid.

What data type is an iterator?

An iterator is an object (like a pointer) that points to an element inside the container. We can use iterators to move through the contents of the container. They can be visualised as something similar to a pointer pointing to some location and we can access content at that particular location using them.

Is iterator type trait?

std::iterator_traits is the type trait class that provides uniform interface to the properties of LegacyIterator types. This makes it possible to implement algorithms only in terms of iterators.

What is template type parameter?

A template parameter is a special kind of parameter that can be used to pass a type as argument: just like regular function parameters can be used to pass values to a function, template parameters allow to pass also types to a function.


1 Answers

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.

like image 123
Barry Avatar answered Oct 13 '22 11:10

Barry