2010-12-02 11 views
8
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 
} 

La pregunta es: ¿Cómo hacer las cinco declaraciones de aserción?¿Cómo comprobar si un parámetro de plantilla es un tipo de iterador o no?

+0

supongo Concept la comprobación podría ayudarte. No es fácil sin embargo. –

+0

¿Realmente ** necesita ** saber? ¿Qué vas a hacer cuando lo descubras? ¿Sería suficiente probar que el tipo implementa, digamos, el operador * y el operador ++? –

+0

@Karl Knechtel: Mi objetivo es: si el tipo T tiene operator * y operator ++ (incluidos los operadores incorporados), entonces is_iterator :: value no será cero. – xmllmx

Respuesta

6
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

¿Por qué es necesario usar "|| is_pointer :: value"? ¿No debería el primer control ya ser suficiente dada la "plantilla static void * test (R *); // Pointer" line? –

+0

@Jan de Vos. No sé por qué Armen cambió mi respuesta. –

+3

No se compila para algunas cosas que no son iteradores (por ejemplo, tipos de funciones), y devuelve verdadero para algunas cosas que no son iteradores (por ejemplo, puntero a función: no existe una serie de funciones y no puede incrementar un puntero a función). Lo suficientemente cerca para la mayoría de los propósitos prácticos, creo. I * think * (no estoy seguro) también puede devolver false para algunas cosas que son iteradores, pero no son punteros ni tienen un typedef 'iterator_type'. Iteradores en 'std' están autorizados a especializar' std :: iterator_traits', creo. –

2

Bueno, se puede comprobar el tipo de tener un typedef anidada llama iterator_category Esto se puede hacer usando SFINAE, y la técnica exacta se puede encontrar en wiki page for SFINAE. Este no es un método del 100%, pero todos los iteradores decentes deberían proporcionar los de tipo común para los iteradores, y el iterator_category es exclusivo de los iteradores. Además, no olvide verificar si TYPE es simplemente un puntero. Los punteros son iteradores.

+0

Lo he intentado durante varias horas y he descubierto que no es un problema trivial al menos en VC++ 2010. – xmllmx

+0

No es trivial, pero es factible –

+0

@Armen Tsirunyan: Lo intenté de muchas maneras y no funcionó. Por favor, ayúdenme – xmllmx

1
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

La prueba no funciona si T no es dereferencable, es decir. a int o similar – metamorphosis

7

entrar aquí unos años más tarde, en la que C++ y C++ 11 14 hacen que sea mucho más fácil de hacer tales cosas. Un iterator es, en su núcleo, algo que es dereferencable, incrementable. Si es un input iterator, entonces también es comparable. Vamos con esto último, ya que se parece a lo que quieres.

La versión más simple sería utilizar void_t: caso

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

Base:

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

caso especialización Válido:

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 necesita confiar en iterator_category o utilizar el tedioso estilo C++ 03 para verificar cosas usando la resolución de sobrecarga. Expresión SFINAE es donde está.


Como señala el Sr. Wakely en los comentarios, [iterator.traits] requiere que:

se requiere que si Iterator es el tipo de un iterador, los tipos

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

se define como el tipo de diferencia del iterador, el tipo de valor y la categoría de iterador, respectivamente.

Así podemos definir nuestro rasgo iterador simplemente comprobar que:

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

Si iterator_traits<T>::iterator_category está enfermo formados, a continuación, T no es un iterador.

+0

Además de ser incrementable y no referenciable, se requieren iteradores válidos para garantizar que 'iterator_traits ' define 'value_type',' difference_type' etc. (proporcionándolos como tipos anidados en el iterador o especializando 'iterator_traits '). Desde [DR 2408] (http://wg21.link/lwg2408) puede verificar con seguridad si hay alguno de ellos en un contexto SFINAE de expresión para decir si 'iterator_traits' cree que el tipo es un iterador. Así que agregaría 'iterator_traits :: iterator_category' a su lista de argumentos' void_t'. –

+0

@JonathanWakely ¿Podría también comprobar ese derecho, en lugar de todo lo demás? – Barry

+0

Sí, probablemente sea seguro suponer que un tipo no proporcionará todos los tipos anidados (o una especialización de 'iterator_traits') sin ser incrementable o desreferenciable. Si un tipo hace eso, puede abofetear al autor y negarse a usar el tipo, en lugar de tratar de hacer que su rasgo lo haga frente :) –

1

El póster original aclaró que en realidad están pidiendo una forma de identificar un InputIterator (ver http://en.cppreference.com/w/cpp/concept/InputIterator) porque quieren poder incrementar y eliminar la referencia del iterador. Esto tiene una solución SFINAE muy simple en C++ estándar 11, p.similar a la de la STL gcc:

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) { /* ... */ }; 

Esto se basa en las clases de tipo de iterador rasgos que definen las typedefs que Armen Tsirunyan pensaba que eran necesaria de los propios iteradores. (Los iteradores pueden proporcionar esos typedefs, sino que también les pueden proporcionar en las clases de rasgos, lo cual es necesario con el fin de utilizar punteros desnudos como iteradores, y se requiere la implementación de la biblioteca estándar para hacerlo.)

Cuestiones relacionadas