Как определить черту типа is_iterator?

  • Я пытаюсь закодировать черту типа is_iterator<T>. Где, когда T является типом итератора, is_iterator<T>::value == true в противном случае - is_iterator<T>::value == false.

  • Что я пробовал до сих пор:


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

РЕЖИМ ДЕМО


Вопрос. Есть ли более правильный способ определения признака типа is_iterator, чем показанный выше?


person 101010    schedule 13.08.2014    source источник
comment
Почему это должен быть целый класс или структура? Почему бы не bool is_iterator(T)?   -  person scohe001    schedule 13.08.2014
comment
@Josh для использования в SFINAE.   -  person 101010    schedule 13.08.2014
comment
Вы можете проверить все требования концепции итератора: en.cppreference.com/w/ cpp/concept/Iterator.   -  person Drax    schedule 13.08.2014
comment
Н.Б. pre-DR 2408 iterator_traits не подходит для SFINAE и создает его для тип без итератора является серьезной ошибкой, а не ошибкой замены   -  person Jonathan Wakely    schedule 13.08.2014
comment
Тип является итератором, если он является либо входным, либо выходным итератором. Определение is_input_iterator<T> и is_output_iterator<T> оставлено читателю в качестве упражнения. :-)   -  person Howard Hinnant    schedule 14.08.2014


Ответы (3)


Как я сказал в комментариях, представленные здесь решения полагаются на непереносимые свойства iterators_traits в некоторых реализациях. В соответствии со стандартами C++03 и C++11, iterator_traits определено только для итераторов (и особого случая указателей), поэтому любое другое использование является поведением undefined. В частности, использование iterator_traits<T>::pointer в контексте SFINAE не гарантирует работу, потому что создание экземпляра iterator_traits<T> будет ссылаться на T::value_type, T::pointer, T::iterator_category и т. д., а это происходит вне «непосредственного контекста», к которому SFINAE не применяется.

C++14 исправит это должен был это исправить (это произошло после C++14 с DR 2408), но для C++11 безопасный способ определить is_iterator — написать трейт, который проверяет все необходимые операции, которые должен определить итератор. Единственные операции, которые должны поддерживать все итераторы, — это operator*, а также пре- и пост-инкремент. К сожалению, могут быть типы, определяющие те операции, которые не являются допустимыми итераторами, поэтому написать правильный трейт довольно сложно.

person Jonathan Wakely    schedule 13.08.2014
comment
Джонатан, относится ли ваш ответ к предложениям в этом вопросе? Или один из них является достаточно хорошим трейтом, который проверяет все необходимые операции? Также, если тип удовлетворяет всем требованиям концепции итератора (что якобы можно проверить) - не является ли он ipso facto итератором, пусть и случайно? - person einpoklum; 16.10.2016
comment
Большинство этих ответов неверны (поскольку они предполагают, что итераторы будут иметь определенные вложенные определения типов), но ответ Барри, вероятно, достаточно хорош. Да, если тип удовлетворяет всем требованиям концепции, то он является моделью этой концепции. - person Jonathan Wakely; 17.10.2016
comment
@JonathanWakely Извините, что поднял старый вопрос. Вы сказали, что ответ Барри, вероятно, достаточно хорош. К сожалению, я не вижу Барри. Вы ссылались на ответ с отметкой времени 13 августа 2014, в 16:26 (в настоящее время под именем пользователя Praetorian)? - person C. Binair; 16.02.2021
comment
@C.Binair Я отвечал на комментарий по поводу другого вопроса, поэтому я говорил об ответе на этот вопрос, то есть этот ответ. - person Jonathan Wakely; 17.02.2021

Ваша проверка завершается ошибкой, если std::iterator_traits<T>::pointer не является указателем, например, если T = std::ostream_iterator<U>.

Я думаю, что лучше проверить, является ли std::iterator_traits<T>::iterator_category либо std::input_iterator_tag, либо производным типом, либо 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 {};
person Praetorian    schedule 13.08.2014
comment
как насчет других тегов итератора? случайный, двунаправленный и т. д. - person ; 04.01.2018
comment
@IonTodirel Все категории итераторов ввода получены из input_iterator_tag, см. это - person Praetorian; 04.01.2018

Я думаю, что нет необходимости проверять какое-либо конкретное свойство вложенных определений типов iterator_traits. Достаточно просто проверить наличие iterator_traits<T>::value_type (или любого другого вложенного typedef, если уж на то пошло), потому что он есть у каждого итератора.

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

К сожалению, это не гарантирует работу в С++ 11, но С++ 14 это исправит (спасибо Jonathan Wakely за указание на это).

person jrok    schedule 13.08.2014
comment
Но iterator_traits не подходит для SFINAE в C++11, поэтому создание экземпляра iterator_traits<X> для типа, не являющегося итератором, является поведением undefined, и ошибка находится не в непосредственном контексте, поэтому ошибка не является ошибкой подстановки (cplusplus.github.io/LWG/lwg-active.html#2408 решает эту проблему) - person Jonathan Wakely; 13.08.2014
comment
К счастью, некоторые реализации годами выпускали iterator_traits, дружественный к SFINAE, потому что так он намного полезнее :) - person Jonathan Wakely; 13.08.2014
comment
Многие почти SFINAE iterator_traits будут ужасно терпеть неудачу на iterator_traits<void*> (и вариантах cv). (Специализация T* поймает это, но экземпляр не скомпилируется) - person Yakk - Adam Nevraumont; 19.08.2014
comment
К сожалению, void const* void volatile* void const volatile*, вероятно, тоже ломаются. - person Yakk - Adam Nevraumont; 19.08.2014