2010-12-04 22 views
15

¿Cómo definiría una función que toma como entrada un iterador sobre cualquier tipo de contenedor STL, pero solo para aquellos de un tipo de plantilla específico? Por ejemplo:Función que toma un iterador STL sobre CUALQUIER contenedor de elementos de un tipo específico

Cualquier iterador de la forma std::list<Unit*>::iteratorostd::vector<Unit*>::iterator

me acaba de definir la función de tomar std::list<Unit*>::iterator, pero si cambiamos a un contenedor STL diferente, yo no quiero tener que cambiar mi código.

¿Hay alguna manera de hacer esto con plantillas u otras?

+2

'std :: list'? Punteros crudos en contenedores STL? Gah! :) –

Respuesta

14

Puede usar un constructo SFINAE como que comprueba si el tipo de datos anidados iterator::value_type es del tipo apropiado.

template<class T, class Iterator> 
typename boost::enable_if<boost::is_same<typename Iterator::value_type, T> >::type 
    f(Iterator i) 
{ 
    /* ... */ 
} 

int main() 
{ 
    std::list<int> l; 
    std::vector<int> v; 

    f<int>(l.begin()); // OK 
    f<int>(v.begin()); // OK 

    std::vector<float> v2; 
    f<int>(v2.begin()); /* Illegal */ 
} 

Esto es lo que entiendo de "una función que toma como entrada un iterador sobre cualquier tipo de contenedor STL, pero sólo a los de un tipo de plantilla específica", pero mi interpretación puede ser errónea.

+0

Esta fue una respuesta muy útil para mí. ¡Gracias! –

6

¿Quiere solo iterar sobre contenedores de my_special_type? En ese caso:

template <bool, typename T> 
struct enable_if; 

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

template <typename T, typename U> 
struct is_same 
{ 
    enum {value = false}; 
}; 

template <typename T> 
struct is_same<T, T> 
{ 
    enum {value = true}; 
}; 

template <typename Iter> 
typename enable_if<is_same<typename Iter::value_type, your_special_type>::value, 
        void>::type 
function(Iter begin, Iter end) 
{ 
    // ... 
} 
+0

¡Interesante! Sin embargo, ¿qué sucede aquí si la función se usa con el tipo incorrecto? Lo siento si esto es trivial. Todavía estoy aprendiendo plantillas. – RyanG

+0

@Ryan: 'enable_if' solo tiene el miembro' type' si el primer parámetro de plantilla es 'true'. Si usa el incorrecto, no compilará. – Bill

+0

@Ryan: la función no * existe * para tipos incorrectos gracias a 'enable_if' :) – fredoverflow

5

Otra forma con las plantillas es activar una aserción estática si la condición no se cumple.

#include <iterator> 
#include <boost/type_traits/is_same.hpp>  
#include <boost/static_assert.hpp> 

template <class Type, class Iter> 
void foo(Iter from, Iter to) 
{ 
    BOOST_STATIC_ASSERT((boost::is_same<typename std::iterator_traits<Iter>::value_type, Type>::value)); 
    //... 
} 

int main() 
{ 
    int arr[10]; 
    foo<int>(arr, arr + 10); //OK 
    foo<double>(arr, arr + 10); //triggers static assertion 
} 

Si desea acabar con las plantillas, entonces también es posible escribir una "any_iterator" utilizando el tipo de borrado. Por ejemplo, como este en: http://stlab.adobe.com/classadobe_1_1any__iterator.html

14

Además de las respuestas existentes que dependen de SFINAE, una aproximación más simple sería definir simplemente la función de tomar un tipo de plantilla arbitrario como el iterador:

template <typename Iter> 
void function(Iter first, Iter last){ 
    Unit* val = *first; 
} 

Este tiene algunos inconvenientes. A diferencia de la solución SFINAE (como boost::enable_if), esto no le proporciona exactamente lo que usted solicitó. Se compila siempre que un objeto del tipo Iter se pueda desreferenciar produciendo un valor convertible a Unit*, que no es exactamente lo mismo. No tiene garantía de que Iter sea un iterador completamente compatible con STL (puede ser simplemente otro tipo que define operator*), o que su tipo de valor es Unit* precisamente.

Por otro lado, es mucho más simple.

0

Bueno, yo usaría un typedef simple en el caso de que sólo se necesita un tipo de depósito en el momento (si he entendido su usecase correctamente este es el caso)

template <class T> 
class ContainerIterator 
{ 
    Container(); 
    public: 
    typedef std::list<T>::iterator type; 
} 

//usage: 
void function(ContainerIterator<YourType>::type begin, ContainerIterator<YourType>::type end) 
{ 
    //... 
} 

para cambiar el contenedor más adelante , simplemente cambie el tipo de contenedor en typedef.

Cuestiones relacionadas