2011-03-24 14 views
12

Me pregunto, si hay una manera de comprobar en tiempo de compilación si un tipo T de algún tipo de iterador es un const_iterator, o no. ¿Hay alguna diferencia en los tipos que definen los iteradores (value_type, pointer, ...) entre los iteradores y los iteradores?cómo detectar si un tipo es un iterador o const_iterator

me gustaría lograr algo como esto:

typedef std::vector<int> T; 

is_const_iterator<T::iterator>::value  // is false 
is_const_iterator<T::const_iterator>::value // is true 

Respuesta

20

C++ 03 Solución:

Como ninguna de las respuestas parece correcta, aquí está mi intento que está trabajando con GCC:

template<typename T> 
struct is_const_pointer { static const bool value = false; }; 

template<typename T> 
struct is_const_pointer<const T*> { static const bool value = true; }; 

template <typename TIterator> 
struct is_const_iterator 
{ 
    typedef typename std::iterator_traits<TIterator>::pointer pointer; 
    static const bool value = is_const_pointer<pointer>::value; 
}; 

Ejemplo:

int main() 
{ 
    typedef std::vector<int>::iterator it_type; 
    typedef std::vector<int>::const_iterator const_it_type; 

    std::cout << (is_const_iterator<it_type>::value) << std::endl; 
    std::cout << (is_const_iterator<const_it_type>::value) << std::endl; 
} 

de salida:

0 
1 

Online Demostración: http://ideone.com/TFYcW

+0

¿Acaso soy yo o ha habido falta en los votos a favor de la comunidad últimamente? :(+1 dijiste que funcionaba, se ve bien –

+3

He seleccionado tu respuesta, ya que tu solución usa std :: iterator_traits <> class –

+2

No funciona cuando 'std :: iterator_traits :: pointer' es un tipo de clase de proxy. Pero si hay una solución para ese caso, tendría que usar SFINAE bastante desagradable. – aschepler

5

Un método que funciona al menos en gcc es a través de la referencia typedef:

struct true_type { }; 
struct false_type { }; 

template<typename T> 
struct is_const_reference 
{ 
    typedef false_type type; 
}; 

template<typename T> 
struct is_const_reference<T const &> 
{ 
    typedef true_type type; 
}; 

template<typename T> 
struct is_const_iterator 
{ 
    typedef typename is_const_reference< 
     typename std::iterator_traits<T>::reference>::type type; 
}; 

puede comprobar que funciona por usando

inline bool test_internal(true_type) 
{ 
    return true; 
} 

inline bool test_internal(false_type) 
{ 
    return false; 
} 

template<typename T> 
bool test(T const &) 
{ 
    return test_internal(typename is_const_iterator<T>::type()); 
} 

bool this_should_return_false(void) 
{ 
    std::list<int> l; 
    return test(l.begin()); 
} 

bool this_should_return_true(void) 
{ 
    std::list<int> const l; 
    return test(l.begin()); 
} 

Con un suficiente alto h nivel de optimización, las últimas dos funciones se deben reducir a return false; y return true;, respectivamente. Al menos lo hacen por mí.

+0

¿Ha probado? ¿El tipo 'iterator' tiene el tipo' reference'? Publica un código de trabajo para ideone! – Nawaz

+0

Sí, he visto la salida del desensamblador y vi que las funciones de prueba devuelven el resultado deseado, como una constante. Yo uso tipos para denotar verdadero y falso aquí, en principio también puedes usar 'static'' const' y lograr el mismo efecto. –

+0

Me refiero a esta sintaxis 'typename T :: reference'. ¿Cómo puedes escribir esto cuando el tipo de 'l.begin()' puede ser 'iterator' o' const_iterator' que en realidad es 'T' en tu sintaxis. Entonces mi pregunta es: ¿cómo funciona 'iterator :: reference'? ¿Lo has probado? por favor, publique un código de trabajo en www.ideone.com. – Nawaz

0

Esto es un poco hacky porque hay que pasar T sí pero funciona (especialización de plantilla, g ++ 4.4.5):

template<typename T, typename S> 
struct is_const_iterator { 
    enum { 
     value = false 
    }; 
}; 

template<typename T> 
struct is_const_iterator<T, typename T::const_iterator> { 
    enum { 
     value = true 
    }; 
}; 

uso como esto:

typedef std::vector<int> T; 
is_const_iterator<T, T::iterator>::value   //is false 
is_const_iterator<T, T::const_iterator>::value  //is true 
+1

Requiere que pase el tipo de contenedor también. ¡Entonces no es una solución, en mi opinión! – Nawaz

3

Con C++ 11, el nuevo encabezado estándar <type_traits> proporciona std::is_const<T>, así la solución de Nawaz puede simplificarse:

template<typename Iterator> 
struct is_const_iterator 
{ 
    typedef typename std::iterator_traits<Iterator>::pointer pointer; 
    static const bool value = 
     std::is_const<typename std::remove_pointer<pointer>::type>::value; 
}; 
4

C++ 11

template<class IT, class T=decltype(*std::declval<IT>())>  
constexpr bool 
is_const_iterator() { 
     return ! std::is_assignable < 
       decltype(*std::declval<IT>()), 
       T 
     >::value; 
} 
+1

Esto va al corazón de la constness de un const_iterator - no se puede asignar al Lo devuelto al eliminar la referenciación del iterador. Esta puede ser la única respuesta que funciona para oddballs como el vector . (Por supuesto, no verifica si es un iterador, o es deref Erencible, pero el OP suponía que era un iterador de algún tipo. Pero para llamarlo "is_const_iterator" probablemente debería verificar tanto la const-ness como el iterator-ness ...) – tony

+0

No creo que esto funcione correctamente si el tipo T representado por el iterador tiene sus operadores de asignación eliminados. –

Cuestiones relacionadas