2009-07-26 4 views
17

Esiste uno standard modo per accedere al contenitore sottostante stack, queue, priority_queue?Esiste un modo per accedere al contenitore sottostante degli adattatori contenitore STL?

Ho trovato un metodo chiamato: _Get_container() in VS2008 realizzazione di stack e queue, ma nessuno per priority_queue! Penso che non sia lo standard comunque.

Inoltre, so che è una domanda stupida! dove posso trovare la documentazione ufficiale della libreria standard?


Solo per chiarimenti, non stavo cercando di rovinare il contenitore sottostante. Tutto quello che stavo cercando di fare è questo:

template <class Container> 
std::ostream& printOneValueContainer(std::ostream& outputstream, Container& container) 
{ 
    Container::const_iterator beg = container.begin(); 

    outputstream << "["; 

    while(beg != container.end()) 
    { 
     outputstream << " " << *beg++; 
    } 

    outputstream << " ]"; 

    return outputstream; 
} 

// stack, queue 
template 
    < class Type 
    , template<class Type, class Container = std::deque<Type> > class Adapter 
    > 
std::ostream& operator<<(std::ostream& outputstream, const Adapter<Type>& adapter) 
{ 
    return printOneValueContainer(outputstream, adapter._Get_container()); 
} 
. 
. 
. 
std::stack<int> iStack; 
. 
. 
std::cout << iStack << std::endl; 

Spero si vede che _Get_container() non è standard, e non c'è nessuno per priority_queue in VS2008 attuazione.

+0

non è esattamente quello che ti serve, ma coda/pila/priority_queue tutti hanno un ** ** protetta membro 'C', che è il contenitore sottostante, quindi se si eredita da uno di questi, è possibile accedere direttamente . –

+0

@Evan Interessante! Questo significa che gli adattatori sono progettati per l'estensione tramite ereditarietà? Se è così, perché nessun agente virtuale? –

+0

Inoltre, i dati protetti sono un no-no nel mio libro - Sono un po 'deluso da questo! –

risposta

14

ho notato la seguente soluzione da qualche parte sul web e lo sto usando nei miei progetti:

template <class T, class S, class C> 
    S& Container(priority_queue<T, S, C>& q) { 
     struct HackedQueue : private priority_queue<T, S, C> { 
      static S& Container(priority_queue<T, S, C>& q) { 
       return q.*&HackedQueue::c; 
      } 
     }; 
    return HackedQueue::Container(q); 
} 

int main() 
{ 
    priority_queue<SomeClass> pq; 
    vector<SomeClass> &tasks = Container(pq); 
    return 0; 
} 

Buon divertimento :).

+0

Mi piace questo hack man. Grazie ;) – AraK

3

No, non esiste un modo standard per farlo. Per quanto riguarda l'accesso allo standard, non è disponibile sul web, devi comprarne una copia! Tuttavia, ci sono varie copie delle bozze available here.

+0

Grazie, Neil, penso che comprerò la documentazione :) – AraK

1

Spero davvero che non ci sia un modo per accedere al contenitore sottostante di una coda di priorità. Se potessi, allora potresti rovinare la struttura heap interna della coda di priorità. In ogni caso il punto di tali adattatori è che essi solo presentano l'interfaccia minima dello stack o della coda e astraggono tutte le altre cose. Quindi, se avevi bisogno di usare altre funzionalità, dovresti aver usato direttamente il contenitore originale.

Per quanto riguarda la documentazione di STL, è possibile consultare la documentazione di STL here di SGI. Ci sono alcune differenze tra lo STL di SGI e quello nello standard C++, ma sono per lo più annotati su quel sito. Inoltre, è una documentazione wiki della libreria C++ che sta diventando più completa.

+0

Vedo il tuo punto. Quello che sto cercando di fare è scrivere una funzione generica che trasmetta i contenuti dello stack, della coda e di priority_queue. È solo per divertimento quindi nessun problema :) – AraK

2

This SGI page è la documentazione più "ufficiale" disponibile online, credo.

Il motivo per cui non è possibile ottenere l'accesso diretto al contenitore sottostante è che l'adattatore modifichi il modello di utilizzo e che i metodi del contenitore sottostante disponibili violino tale nuovo modello di utilizzo. Ad esempio:

2 Questa restrizione è l'unico motivo per cui esiste la coda. Qualsiasi contenitore che sia sia una sequenza di inserimento anteriore che una sequenza di inserimento posteriore può essere utilizzato come coda; deque, ad esempio, ha funzioni membro front, back, push_front, push_back, pop_front e pop_back L'unico motivo per utilizzare la coda dell'adattatore contenitore anziché il deque contenitore è di rendere chiaro che si stanno eseguendo solo operazioni di coda e nessun altro operazioni. http://www.sgi.com/tech/stl/queue.html

Se si desidera ottenere intorno a questa caratteristica del progetto, si può fare qualcosa di simile:

template <typename T> 
class clearable_queue : public std::queue<T> 
{ 
public: 
    void clear() { c.clear(); } 
}; 
+0

Di solito dovresti comporre i contenitori piuttosto che derivarne. Ad esempio, non hanno distruttori virtuali. – GManNickG

+0

concordato. Tuttavia in questo caso non ci interessano i distruttori e, usando l'ereditarietà, evitiamo di dover definire l'interfaccia 'queue' come metodi proxy. –

2

Come regola generale, qualsiasi identificatore che inizia con un carattere di sottolineatura è un'estensione specifico del fornitore o un dettaglio di implementazione. Quindi, _Get_container() è solo un'aggiunta fatta da Microsoft perché ne ha semplificato l'implementazione. Non è destinato a essere utilizzato.

Per quanto riguarda dove trovare la documentazione, è suddivisa in più parti.

La fonte autorevole è, ovviamente, lo standard del linguaggio. Come diceva Neil Butterworth, ci sono bozze di copie disponibili online gratuite (che sono comunque molto utili.) Le differenze da quelle alla versione finale sono davvero minime). In alternativa, puoi acquistare una copia. Dovrebbe essere disponibile da qualsiasi organizzazione che rappresenti l'ISO nel tuo paese (e probabilmente anche da un altro insieme di altre fonti). Il documento che stai cercando è ISO/IEC 14882:2003 Programming Language C++. (14882 è il numero standard Il 2003 è l'anno dell'ultima revisione.Se trovi la versione del 1998, puoi usare anche quella: le differenze sono davvero ridicolmente piccole tra i due, e in pratica si tratta solo di alcuni chiarimenti. probabilmente meglio stare lontano dalle bozze per C++ 0x, poiché le modifiche sono molto più estese)

A parte ciò, ogni implementazione della libreria standard è necessaria per documentare un gran numero di dettagli (definiti dall'implementazione comportamento, cose che non sono specificate nello standard, ma che sono lasciate al programma di implementazione della libreria). Inoltre, molti di loro hanno anche messo a punto una documentazione dettagliata dell'intera biblioteca.

Microsoft ha documentazione dettagliata disponibile su MSDN. La documentazione rispetta lo standard e contrassegna chiaramente tutte le estensioni non standard in modo da sapere quale è.

SGI ha anche la documentazione in linea (sebbene sia più vecchia e in alcuni casi non del tutto accurata).

IBM ha documentazione simile disponibile sul loro sito Web, e credo che anche GCC lo faccia.

+0

Grazie Jalf che è stato utile :) – AraK

+0

Oh, non sapevo nemmeno che questa era la tua domanda. Pensavo che tu sapessi già questa roba. ;) – jalf

-2

dove è possibile trovare la documentazione ufficiale della libreria standard?

Lo standard C++ è disponibile in copertina rigida, ISBN 0470846747. Bozze di standard sono ampiamente disponibili in formato PDF, ma dovrete stare attenti a abbinarli con le versioni ufficiali (C++ 98,03, 11 o 14). Le bozze correnti superano lo standard C++ 14.

7

L'ho menzionato in un commento, ma dopo qualche riflessione sembra essere una soluzione OK. queue/stack/priority_queue (ovvero tutte le classi di adattatori) hanno tutti un protected membro c che è il contenitore sottostante (vedere ISO/IEC 14882: 2003 sezione 23.2.2.4), quindi se si eredita da uno di questi, si può accedervi direttamente

So che la saggezza tipica è quella di non ereditare dai contenitori STL a causa di dvd non virtuali, ma questo caso è un'eccezione. L'obiettivo non è sovraccaricare la funzionalità, ma creare estensioni secondarie all'interfaccia dell'adattatore. Ecco un esempio di aggiunta della possibilità di accedere al contenitore sottostante.

#include <queue> 
#include <iostream> 

template <class Container> 
class Adapter : public Container { 
public: 
    typedef typename Container::container_type container_type; 
    container_type &get_container() { return this->c; } 
}; 

int main() { 
    typedef std::queue<int> C; 
    typedef Adapter<C> Container; 

    Container adapter; 

    for(int i = 0; i < 10; ++i) { 
     adapter.push(i); 
    } 

    Container::container_type &c = adapter.get_container(); 
    for(Container::container_type::iterator it = c.begin(); it != c.end(); ++it) { 
     std::cout << *it << std::endl; 
    } 
} 

Purtroppo, si dovrà ricorrere a digitare-giochi di parole per "aggiornare" un std::queue<int> * esistente a un Adapter<std::queue<int> > *, senza giochi di parole tipo. Tecnicamente, questo sarebbe probabilmente funzionerà bene ... ma vi consiglio contro di essa:

typedef std::stack<int> C; 
    typedef Adapter<C> Container; 
    C stack; 
    // put stuff in stack 
    Container *adapter = reinterpret_cast<Container *>(&stack); 
    Container::container_type &c = adapter->get_container(); 
    // from here, same as above   

Così mi consiglia di utilizzare typedef per rendere più facile da scambiare tra i due. (Nel mio esempio si noti anche che è necessario modificare 1 riga per modificarla da queue a stack a causa dell'uso liberale di typedef s).

3

Sulla base del accepted answer, un approccio più generale:

template <class ADAPTER> 
typename ADAPTER::container_type & get_container (ADAPTER &a) 
{ 
    struct hack : ADAPTER { 
     static typename ADAPTER::container_type & get (ADAPTER &a) { 
      return a.*&hack::c; 
     } 
    }; 
    return hack::get(a); 
} 

Come ho imparato da this answer, .*& è in realtà due operatori, in cui il puntatore risultante da &hack::c (che è di tipo ADAPTER::container_type ADAPTER::*) è il bersaglio o la Operatore .* per recuperare il contenitore sottostante. hack ha accesso al membro protetto, ma dopo aver ottenuto il puntatore, le protezioni vengono perse. Quindi è consentito a.*(&hack::c).

0

È possibile scrivere una sottoclasse per recuperare la variabile membro c. Vedi questo commento da libstdC++.

protected: 
/** 
* 'c' is the underlying container. Maintainers wondering why 
* this isn't uglified as per style guidelines should note that 
* this name is specified in the standard, [23.2.3.1]. (Why? 
* Presumably for the same reason that it's protected instead 
* of private: to allow derivation. But none of the other 
* containers allow for derivation. Odd.) 
*/ 
_Sequence c;