Как проверить, является ли параметр шаблона типом итератора или нет? - PullRequest
10 голосов
/ 02 декабря 2010
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
}

Вопрос в следующем: Как заставить пять утверждений утверждений пройти?

Ответы [ 5 ]

11 голосов
/ 14 июля 2015

Приход сюда через несколько лет, где C ++ 11 и C ++ 14 значительно облегчают такие вещи. Итератор по своей сути является разыменовываемым, инкрементным. Если это входной итератор , то также сопоставим. Давайте пойдем с последним - так как это выглядит так, как вы хотите.

Простейшей версией было бы использование void_t:

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

Базовый корпус:

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

Действительная специализация кейса:

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 { };

Псевдоним:

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

Нет необходимости полагаться на iterator_category или использовать утомительный стиль проверки C ++ 03 с использованием разрешения перегрузки. Выражение SFINAE там, где оно есть.

<ч />

Как указывает г-н Уэйкли в комментариях, [iterator.traits] требует, чтобы:

требуется, чтобы, если Iterator был тип итератор, типы

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

определяется как тип разницы итератора, тип значения и категория итератора, соответственно.

Таким образом, мы можем определить нашу черту итератора, чтобы просто проверить это:

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 { };

Если iterator_traits<T>::iterator_category плохо сформирован, то T не является итератором.

6 голосов
/ 02 декабря 2010
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 *); 
};
2 голосов
/ 02 декабря 2010

Ну, вы можете проверить, что для типа есть вложенная typedef с именем iterator_category Это можно сделать с помощью SFINAE, а точную технику можно найти на вики-странице для SFINAE.Это не 100% метод, но все приличные итераторы должны предоставлять общие определения типов для итераторов, а iterator_category является уникальной для итераторов.Также не забудьте проверить, является ли TYPE просто указателем.Указатели являются итераторами.

1 голос
/ 24 октября 2016

Оригинальный плакат пояснил, что на самом деле они просят указать способ идентификации InputIterator (см. http://en.cppreference.com/w/cpp/concept/InputIterator), потому что они хотят иметь возможность увеличивать и разыменовывать итератор. Это очень простое решение SFINAE в стандарте C ++ 11, например, аналогично 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) { /* ... */ };

Это опирается на классы признаков типа итератора, которые определяют определения типов, которые, как считал Армен Цирунян, требовались от самих итераторов. (Итераторы могут предоставлять эти определения типов, но они также могут предоставлять их в классах признаков, что необходимо для использования в качестве итераторов обнаженных указателей, и для этого требуются реализации стандартной библиотеки.)

1 голос
/ 18 июля 2012
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 { };
...