2012-11-13 14 views
16

Dato un iteratore, è possibile recuperare/utilizzare la funzione di confronto corretta per la raccolta a cui si riferisce questo iteratore?Richiamare la funzione di confronto del contenitore dato un iteratore

Per esempio, supponiamo che sto scrivendo un algoritmo generico:

template <class InIt, class T> 
void do_something(InIt b, InIt e, T v) { 
    // ... 
} 

Ora, diciamo che voglio fare qualcosa di semplice, come trovare v in [b..e). Se b e e sono iteratori su un std::vector, posso semplicemente usare if (*b == v) .... Supponiamo, tuttavia, che b e e siano iteratori su un std::map. In questo caso, I dovrebbe solo confrontare le chiavi , non l'intero valore di ciò che è contenuto nella mappa.

Quindi la domanda è, dati quegli iteratori sulla mappa, come faccio a recuperare la funzione di confronto di quella mappa che confronta solo le chiavi? Allo stesso tempo, non voglio credere ciecamente che sto lavorando con uno map. Ad esempio, se gli iteratori puntano a un set, vorrei utilizzare la funzione di confronto definita per tale set. Se hanno indicato un vector o deque, probabilmente dovrei usare ==, perché quei contenitori non avranno una funzione di confronto definita.

Oh, quasi dimenticato: mi rendo conto che in molti casi, un container avrà solo un equivalente di operator< anziché operator== per gli elementi in esso contenuti - Sto perfettamente bene con l'essere in grado di usarlo.

+0

Non c'è tempo per scrivere una risposta in questo momento, ma questo potrebbe aiutare http://en.cppreference.com/w/cpp/container/map/key_comp. Hmm, non molto, però. La parte difficile è ottenere il contenitore. –

+0

Sono confuso per il tuo caso 'set'. Il comparatore di un 'set' deve essere un ordine totale, che non è il caso di' operator == '. – pmr

+0

Non è lo stesso, sia che si confronti solo la chiave di una voce della mappa o l'intera voce? [Modifica] Non importa, ovviamente non lo è - potresti non avere un'operazione di uguaglianza sulla parte mappata. –

risposta

6

Non esiste un modo standard per eseguire la mappatura da un iteratore al tipo di contenitore sottostante (se esiste un tale contenitore). Potresti essere in grado di utilizzare alcune euristiche per cercare di determinare quale contenitore, anche se ciò non sarà semplice e probabilmente non garantito.

Ad esempio, è possibile utilizzare un metafunction per determinare se il * value_type * è std::pair<const K, T>, che è un suggerimento che questo potrebbe essere un std::map e dopo l'estrazione dei tipi K e T provare a utilizzare un metafunction per determinare se il tipo di l'iteratore e il tipo di std::map<K,T,X,Y>::iterator o std::map<K,T,X,Y>::const_iterator corrispondono per una particolare combinazione di X, Y.

Nel caso della mappa che potrebbe essere sufficiente a determinare (cioè indovinate con un'alta probabilità di successo) che l'iteratore si riferisce ad una std::map, ma si dovrebbe notare che, anche se si può usare questo e ancora di estrarre il tipo X del comparatore, che non è sufficiente per replicare il comparatore nel caso generale. Mentre i comparatori non comuni (e non raccomandati) possono avere lo stato, e non si saprebbe quale sia lo stato particolare del comparatore senza avere accesso direttamente al contenitore.Inoltre, ci sono casi in cui questo tipo di euristica non aiuta nemmeno, in alcune implementazioni di std::vector<> il tipo iteratore è direttamente un puntatore, e in tal caso non è possibile distinguere tra un 'iteratore' in un array e un iteratore in un std::vector<> degli stessi tipi sottostanti.

11

Gli iteratori non devono essere collegati ai contenitori, quindi non forniscono dettagli sui contenitori a cui non sono necessariamente connessi. Questa è l'astrazione essenziale dell'iteratore: gli iteratori delimitano le sequenze, indipendentemente da dove provenga la sequenza. Se è necessario conoscere i contenitori, è necessario scrivere algoritmi che accettano contenitori.

+2

Esempio: alcuni 'string' e' vector' hanno implementato iteratori come puntatori nudi (sfruttando il buffer sottostante sequenziale e contiguo). –

+0

Anche se si prendono contenitori come argomenti, è comunque necessario fare affidamento sui trucchi per distinguere una 'Sequenza' da un 'AssociativoContainer'. Suppongo che sia difficile scrivere un 'is_associative_container' che funzioni in modo affidabile. – pmr

+0

@ pmr: Quel tratto è in realtà semplice se si desidera supportare solo contenitori standard ... è sufficiente determinare se il tipo è uno di 'std :: map',' std :: multimap', 'std :: set ',' std :: multiset' (e le versioni non ordinate se devi supportare quelle, anche se il concetto di * comparatore * non è chiaro lì) –

3

Purtroppo gli iteratori non sempre conoscono il contenitore che li contiene (e talvolta non sono affatto in un contenitore standard). Anche lo iterator_traits ha solo informazioni su value_type che non specificano specificamente come confrontare.

Invece, prendiamo ispirazione dalla libreria standard. Tutti i contenitori associativi (map, ecc.) Hanno i propri metodi find anziché utilizzare std::find. E se tu do devi usare std::find su un contenitore del genere, non lo fai: usi find_if.

Sembra che la soluzione sia che per i contenitori associativi è necessario un do_something_if che accetta un predicato dicendogli come confrontare le voci.

Problemi correlati