2009-07-09 15 views
11

tengo:iterador "genérica" ​​en C++

void add_all_msgs(std::deque<Message>::iterator &iter); 

¿Cómo puedo hacer que la función "genérico", por lo que puede tomar cualquier tipo de inputiterators? Realmente no me importa si está iterando un deque, un vector u otra cosa, siempre y cuando el iterador itere Mensaje. - ¿Es esto lo más sencillo posible en C++?

Respuesta

6
template<class InputIterator> 
void add_all_msgs(InputIterator iter); 

Uso:

std::deque<Message> deq; 
add_all_msgs(deq.begin()); 
+5

La convención consiste en tomar iteradores por valor en lugar de por referencia.Aparte de que los iteradores son normalmente "pequeños" de todos modos, la razón para esto (afaik) es permitir que la persona que llama transmita un valor temporal, como un valor de retorno de std :: back_inserter. C++ 0x me ayuda con esto en al menos dos formas en las que puedo pensar. –

+0

Heh, o el valor de retorno de "begin" empieza a pensarlo. El código de "uso" anterior no compila: "inicialización no válida de referencia no constante de tipo 'blah' de un temporal de tipo 'blah'". –

+1

El OP dijo explícitamente: "siempre que el iterador repita el mensaje". Esta solución, y la mayoría de las demás, ignoraron por completo este requisito. –

0
#include <deque> 
#include <vector> 
#include <list> 
#include <string> 
using namespace std; 

template<typename T> 
void add_all_msgs(T &iter) 
{ 

} 

int _tmain(int argc, _TCHAR* argv[]) 
{ 
    std::deque<string>::iterator it1; 
    std::vector<string>::iterator it2; 
    std::list<string>::iterator it3; 

    add_all_msgs(it1); 
    add_all_msgs(it2); 
    add_all_msgs(it3); 


    return 0; 
} 
+0

¿Y los add_all_msgs tienen que ser una plantilla más o menos? ¿Los iteradores no son "polimórficos"? – nos

+0

@noselasd: los iteradores no se derivan de una clase base común, si eso es lo que quieres decir. –

+0

Por lo tanto, son polimórficos (a través de la tipificación de pato estática), simplemente no de la forma en que podría pensar en polimorfismo en, digamos, Java. –

12
template <typename Iterator> 
void add_all_messages(Iterator first, Iterator last) 

uso:

vector<message> v; 
add_all_messages(v.begin(), v.end()); 

Es necesario especificar el final, de lo contrario no saber cuándo parar! También le da la flexibilidad de agregar solo un subrango de un contenedor.

+0

Bueno, en realidad asumí (a diferencia de aJ) que el iterador se usa como un iterador de salida, en cuyo caso, el iterador final es superfluo. :-P –

+0

Oh, espera, el OP dice que es un iterador de entrada. Punto a favor. :-P –

5

Si desea que el compilador compruebe si el iterador realmente se refiere a los objetos Message, puede utilizar una técnica como la siguiente.

template <typename InputIterator, typename ValueType> 
struct AddAllMessages { }; 

template <typename InputIterator> 
struct AddAllMessages<InputIterator, Message> { 
    static void execute(const InputIterator &it) { 
    // ... 
    } 
}; 

template <typename InputIterator> 
void add_all_msgs(const InputIterator &it) { 
    AddAllMessages<InputIterator, 
       typename std::iterator_traits<InputIterator>::value_type>::execute(it); 
} 
1

un poco más sencillo que el anterior (en la que aprovecha las bibliotecas existentes):

#include <boost/static_assert.hpp> // or use C++0x static_assert 
#include <boost/type_traits/is_same.hpp> 

template <typename InputIterator> 
void add_all_msgs(InputIterator it) { 
    BOOST_STATIC_ASSERT((boost::is_same< 
     typename std::iterator_traits<InputIterator>::value_type, 
     Message>::value)); 
    // ... 
2

Si no desea crear plantillas de su función add_all_msgs, se pueden utilizar adobe::any_iterator:

typedef adobe::any_iterator<Message, std::input_iterator_tag> any_message_iterator; 
void add_all_msgs(any_message_iterator begin, any_message_iterator end); 
+0

Nifty. [15chars] –

+0

Iré con plantillas de add_all_msgs por ahora, aunque esto es lo que idealmente quería. – nos

+2

La versión de la plantilla es sin duda más eficiente, con any_iterator tiene una llamada de función virtual en cada operación (es decir, pasar a la siguiente posición y desreferenciación) frente a una llamada de función en línea. –

1

Es difícil tener un polimorfismo dinámico con iteradores de estilo C++. operator++(int) devuelve por valor, lo que afaik es intratable: no puede tener una función de miembro virtual que devuelve *this por valor sin que se corte.

Si es posible, recomiendo usar plantillas como todo el mundo dice.

Sin embargo, si usted no necesita el polimorfismo dinámico, por ejemplo, porque no se puede exponer a la implementación de add_all_msgs como plantilla haría, entonces yo creo que se podría pretender ser Java, así:

struct MessageIterator { 
    virtual Message &get() = 0; 
    virtual void next() = 0; 
    // add more functions if you need more than a Forward Iterator. 
    virtual ~MessageIterator() { }; // Not currently needed, but best be safe 
}; 

// implementation elsewhere. Uses get() and next() instead of * and ++ 
void add_all_msgs(MessageIterator &it); 

template <typename T> 
struct Adaptor : public MessageIterator { 
    typename T::iterator wrapped; 
    Adaptor(typename T::iterator w) : wrapped(w) { } 
    virtual Message &get() { 
     return *wrapped; 
    } 
    virtual void next() { 
     ++wrapped; 
    } 
}; 

int main() { 
    std::deque<Message> v; 
    Adaptor<std::deque<Message> > a(v.begin()); 
    add_all_msgs(a); 
} 

He comprobado que esto compila, pero no lo he probado y nunca he usado este diseño. Tampoco me he molestado con la estupidez. En la práctica, es probable que desee un const Message &get() const. Y, por el momento, el adaptador no tiene forma de saber cuándo detenerse, pero tampoco el código con el que comenzó, así que ignoré eso también. Básicamente necesitaría una función hasNext que compare wrapped con un iterador final proporcionado al constructor.

Es posible que pueda hacer algo con una función de plantilla y const references, para que el cliente no tenga que saber o declarar ese desagradable tipo de adaptador.

[Editar: pensándolo bien, es probable que sea mejor tener una plantilla de función stub add_all_msgs, que envuelve su parámetro en un adaptador y luego llama al real_add_all_msgs. Esto oculta completamente el adaptador del cliente.]