2012-01-05 23 views
6

Tengo una función que necesita enumerar un iterador varias veces, pero according to MSDN, "Una vez que incrementa cualquier copia de un iterador de entrada, ninguna de las otras copias puede compararse, eliminarse o incrementarse con seguridad de allí en adelante". Para facilitar las cosas, en lugar de crear una implementación separada para los iteradores que duplican los datos y enumeran la copia, quiero restringir mi método a solo tomar adelante iteradores y rechazar iteradores de entrada estáticamente .Cómo restringir un iterador a ser un iterador directo?

Ahora mismo tengo algo como:

template<typename It, typename TCallback /*signature: bool(value_type)*/> 
bool EnumerateTwice(const It &begin, const It &end, TCallback callback) 
{ 
    for (It it = begin; it != end; ++it) 
     if (!callback(*it)) 
      return false; 
    for (It it = begin; it != end; ++it) 
     if (!callback(*it)) 
      return false; 
    return true; 
} 

pero nada restringe It a ser un iterador hacia adelante.

¿Cómo coloco esa restricción en la función de plantilla? (C++ 03)

Respuesta

5

Puede utilizar SFINAE y reemplazar bool por:

typename enable_if< 
    is_same<typename std::iterator_traits<It>::iterator_category, 
      std::forward_iterator_tag>::value, 
    bool>::type 

Es posible que necesite para definir is_same y enable_if sí mismo si no quiere tirar de ellos de Boost o TR1:

template <typename A, typename B> 
struct is_same { static const bool value = false; }; 

template <typename T> 
struct is_same<T, T> { static const bool value = true; }; 

template <bool, typename> struct enable_if { }; 
template <typename T> struct enable_if<true, T> { typedef T type; }; 
+0

+1 ¡Oooooo no sabía que los iteradores están etiquetados! Eso es bastante útil. ¡Muchas gracias! :) – Mehrdad

+9

Esto rechazará los iteradores bidireccionales y de acceso aleatorio. Utilice 'is_base_of' en lugar de' is_same' para la semántica adecuada. – ildjarn

+0

@ildjarn: Oh sí, buen punto; ¡Gracias! – Mehrdad

4

No se ha probado, pero podría intentar algo en la línea de:

template<typename It, typename TCallback /*signature: bool(value_type)*/> 
bool EnumerateTwice_Interal(const It &begin, const It &end, TCallback callback, 
     std::forward_iterator_tag) 
{ 
    //do your things 
} 

template<typename It, typename TCallback /*signature: bool(value_type)*/> 
bool EnumerateTwice(const It &begin, const It &end, TCallback callback) 
{ 
    EnumerateTwice_Internal(begin, end, callback, 
     typename std::iterator_traits<It>::iterator_category()); 
} 
+1

+1 para evitar SFINAE: esta es una IMO más limpia. – ildjarn

3

Usted puede hacer esto utilizando std::enable_if:

#include <iterator> 
#include <type_traits> 
#include <utility> 

template <typename It, typename TCallback> 
typename std::enable_if<std::is_base_of<std::forward_iterator_tag, 
         typename std::iterator_traits<It>::iterator_category>::value, 
        bool>::type 
EnumerateTwice(It begin, It end, TCallback) { 
    ... 
} 

Este utiliza la clase de C++ 11, pero todo esto se puede hacer en C++ 03 también.

0

Para ampliar la respuesta de Rodrigo - me encontré con esta solución, y pensé que vale la pena mencionar:

struct True { unsigned char _[2]; operator bool() const { return true; } }; 
char is_forward_iterator(std::input_iterator_tag const *) { return 0; } 
True is_forward_iterator(std::forward_iterator_tag const *) { return True(); } 

Ahora, si usted quiere comprobar que el interior de alguna función, se puede decir:

if (is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0))) 
{ 
    ... 
} 

y si desea comprobar que dentro de las plantillas, se puede comprobar:

sizeof(
    is_forward_iterator(static_cast<iterator_traits<It>::iterator_category*>(0)) 
) > 1 

con el primario La ventaja de este método es que evita declarar plantillas (p. para una mejor velocidad de compilación).

Cuestiones relacionadas