Как проверить, является ли параметр шаблона типом итератора или нет?
Вопрос
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
}
Вопрос в том: Как сделать пять заявлений о выдаче утверждений?
Решение
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 *);
};
Другие советы
Приезжая сюда несколько лет спустя, где 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
не итератор.
Что ж, вы можете проверить, чтобы получить тип, чтобы иметь вложенный Typedef под названием iterator_category
Это можно сделать с помощью SFINAE
, и точную технику можно найти в Вики страница для SFINAE
. Анкет Это не 100% метод, но все приличные итераторы должны предоставлять общие типы для итераторов, а iterator_category - это уникальная для итераторов. Также не забудьте проверить, является ли тип просто указатель. Указатели итераторы.
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 { };
Первоначальный плакат пояснил, что они на самом деле просят способ идентифицировать входчик (см. 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) { /* ... */ };
Это опирается на классы черт типа итератора, которые определяют типы, которые Армен Цируньян мысли, требовались от самих итераторов. (Итераторы могу Предоставьте эти Typedefs, но они также могут предоставить их в классах признаков, что необходимо для использования обнаженных указателей в качестве итераторов, и для этого необходимы стандартные реализации библиотеки.)