2009-07-09 21 views
11

ho:iteratore "generico" in C++

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

Come posso fare che la funzione "generico", in modo che possa prendere qualsiasi tipo di inputiterators? Non mi interessa davvero se stia iterando un deque, un vettore o qualcos'altro, fintanto che l'iteratore sta iterando Message's. - Questo è possibile in C++?

risposta

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

Usage:

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

La convenzione deve prendere gli iteratori in base al valore anziché al riferimento.A parte il fatto che gli iteratori sono normalmente "piccoli" in ogni caso, la ragione di questo (afaik) è di consentire al chiamante di passare un valore temporaneo, come un valore di ritorno da std :: back_inserter. C++ 0x aiuta in questo almeno in due modi a cui riesco a pensare. –

+0

Heh, o il valore restituito da "begin" viene a pensarci. Il precedente codice "usage" non viene compilato: "inizializzazione non valida di riferimento non const di tipo" blah "da un carattere temporaneo di tipo" blah "". –

+1

L'OP ha esplicitamente detto: "fino a quando l'iteratore sta iterando il messaggio". Questa soluzione, e la maggior parte delle altre, ha completamente ignorato questo 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

E l'add_all_msgs deve essere un modello più o meno? Gli iteratori non sono "polimorfi"? – nos

+0

@noselasd: gli iteratori non derivano da una classe base comune, se questo è ciò che intendi. –

+0

Quindi essi sono _ri_ polimorfi (tramite la tipizzazione anatra statica), non solo nel modo in cui si potrebbe pensare al polimorfismo, per esempio, in Java. –

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

utilizzo:

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

È necessario specificare alla fine, altrimenti non si sa quando smettere! Ti dà anche la flessibilità di aggiungere solo un subrange di un contenitore.

+0

Beh, in realtà ho assunto (a differenza di aJ) che l'iteratore sia usato come un iteratore di output, nel qual caso l'iteratore finale è superfluo. :-P –

+0

Oh aspetta, l'OP dice che è un iteratore di input. Punto preso. :-P –

5

Se si desidera che il compilatore verifichi che l'iteratore si riferisca effettivamente agli oggetti Message, è possibile utilizzare una tecnica simile alla seguente.

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

leggermente più semplice che quanto sopra (in quanto sfrutta le librerie esistenti):

#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

Se non si desidera templatize la funzione add_all_msgs, è possibile utilizzare 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

Per ora andrò con il template di add_all_msgs, anche se questo è quello che avrei desiderato idealmente. – nos

+2

La versione del modello è sicuramente più efficiente, con any_iterator si ha una chiamata di funzione virtuale su ogni operazione (ad esempio passando alla posizione successiva e al dereferenziamento) rispetto a una chiamata di funzione inline. –

1

È difficile avere un polimorfismo dinamico con gli iteratori in stile C++. operator++(int) restituisce per valore, che afaik è intrattabile: non è possibile avere una funzione membro virtuale che restituisce *this in base al valore senza essere affettato.

Se possibile, è consigliabile utilizzare i modelli come tutti gli altri dicono.

Tuttavia, se avete bisogno di polimorfismo dinamico, ad esempio perché non si può esporre l'attuazione di add_all_msgs come modello avrebbe fatto, allora io credo che si possa fingere di essere Java, in questo modo:

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

Ho controllato che questo compili, ma non l'ho ancora testato e non ho mai usato questo design prima. Inoltre non mi sono preoccupato della costanza: in pratica probabilmente vorrai un const Message &get() const. E al momento l'adattatore non ha modo di sapere quando fermarsi, ma poi nemmeno il codice con cui hai iniziato, quindi ho ignorato anche questo. Fondamentalmente avresti bisogno di una funzione hasNext che confronta wrapped con un iteratore finale fornito al costruttore.

Potrebbe essere possibile eseguire un'operazione con una funzione di modello e riferimenti const, in modo che il client non debba conoscere o dichiarare quel tipo di adattatore sgradevole.

[Modifica: a pensarci bene, probabilmente è meglio disporre di un modello di funzione stub add_all_msgs, che avvolge i suoi parametri in un adapter e quindi chiama real_add_all_msgs. Questo nasconde completamente l'adattatore dal client.]