2009-07-26 10 views
17

¿Existe una norma manera para acceder al contenedor subyacente de stack, queue, priority_queue?¿Hay alguna manera de acceder al contenedor subyacente de los adaptadores de contenedor STL?

He encontrado un método llamado: _Get_container() en VS2008 implementación de stack y queue, pero nadie para priority_queue! Creo que no es estándar de todos modos.

Además, sé que es una pregunta tonta! ¿Dónde puedo encontrar la documentación oficial de la biblioteca estándar?


Solo por aclaración, no estaba tratando de meter la pata con el contenedor subyacente. Todo lo que yo estaba tratando de hacer es lo siguiente:

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; 

espero que vea que _Get_container() no es estándar, y no hay nadie para priority_queue en VS2008 aplicación.

+0

no es exactamente lo que necesita, pero queue/stack/priority_queue tiene un ** protected ** member 'c' que es el contenedor subyacente, por lo que si hereda de alguno de estos, puede acceder directamente . –

+0

@Evan Interesante! ¿Esto significa que los adaptadores están destinados a la extensión a través de la herencia? Si es así, ¿por qué no hay dtor virtual? –

+0

Además, los datos protegidos son un no-no en mi libro. ¡Estoy un poco decepcionado con esto! –

Respuesta

14

vi a la siguiente solución en algún lugar de la web y lo estoy usando en mis proyectos:

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

Diviértase :).

+0

Me gusta este hackman. Gracias ;) – AraK

3

No, no hay una forma estándar de hacerlo. En cuanto al acceso a la norma, no está disponible en la web, ¡tienes que comprar una copia! Sin embargo, hay varias copias de borradores available here.

+0

Gracias Neil, creo que compraré la documentación :) – AraK

1

Espero que no haya una forma de acceder al contenedor subyacente de una cola de prioridad. Si pudiera, podría estropear la estructura interna del montón de la cola de prioridad. En cualquier caso, el punto de esos adaptadores es que solo le presentan la interfaz mínima de la pila o cola, y resumen todas las demás cosas. Entonces, si necesita usar alguna otra característica, debería haber usado el contenedor original directamente.

En cuanto a la documentación de la STL, puede consultar la documentación de la STL here de SGI. Hay algunas diferencias entre el STL de SGI y el estándar de C++, pero en su mayoría se observan en ese sitio. Además, es una documentación wiki de la biblioteca C++ que se está volviendo más completa.

+0

Veo su punto. Lo que intento hacer es escribir una función genérica que transmita los contenidos de la pila, la cola y la prioridad_cola. Es solo por diversión así que no hay problema :) – AraK

2

This SGI page es la documentación más "oficial" disponible en línea, creo.

La razón por la que no se puede obtener acceso directo al contenedor subyacente es que el adaptador cambia el patrón de uso, y tener los métodos del contenedor subyacente disponible violaría ese nuevo patrón de uso. Por ejemplo:

2 Esta restricción es la única razón por la que existe la cola. Cualquier contenedor que sea a la vez una secuencia de inserción frontal y una secuencia de inserción posterior puede usarse como una cola; deque, por ejemplo, tiene funciones miembro front, back, push_front, push_back, pop_front y pop_back La única razón para usar la cola del adaptador del contenedor en lugar del contenedor deque es para dejar en claro que está realizando solo operaciones de cola, y ninguna otra operaciones. http://www.sgi.com/tech/stl/queue.html

Si desea obtener alrededor de esta característica de diseño, se puede hacer algo como:

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

Por lo general, debes componer contenedores en lugar de derivar de ellos. No tienen destructores virtuales, por ejemplo. – GManNickG

+0

De acuerdo. Sin embargo, en este caso no nos importan los destructores, y al usar la herencia evitamos tener que definir la interfaz 'queue' como métodos proxy. –

2

Como regla general, cualquier identificador que comienza con un guión es una extensión específica del proveedor o un detalle de implementación. Entonces, _Get_container() es solo una adición hecha por Microsoft porque simplificó su implementación. No está destinado a ser utilizado.

En cuanto a dónde encontrar la documentación, se divide en varias partes.

La fuente autorizada es, por supuesto, el estándar de idioma. Como dijo Neil Butterworth, hay copias en borrador disponibles de forma gratuita en línea (que siguen siendo muy útiles. Las diferencias con respecto a la versión final son realmente mínimas). Alternativamente, puedes comprar una copia. Debería estar disponible desde cualquier organización que represente ISO en su país (y probablemente también desde otras fuentes). El documento que está buscando es ISO/IEC 14882:2003 Programming Language C++. (14882 es el número estándar. 2003 es el año de la última revisión. Si te encuentras con la versión de 1998, puedes usar eso también. Las diferencias son realmente ridículas entre las dos, y básicamente solo equivale a algunas aclaraciones. probablemente sea mejor mantenerse alejado de los borradores para C++ 0x, ya que los cambios allí son mucho más extensos)

Además, cada implementación de la biblioteca estándar es necesaria para documentar una gran cantidad de detalles (definida por la implementación) comportamiento, cosas que no están especificadas en el estándar, pero que se dejan al implementador de la biblioteca). Y, además, la mayoría de ellos también presentan una documentación detallada de toda la biblioteca.

Microsoft tiene documentación detallada disponible en MSDN. La documentación respeta el estándar y marca claramente todas las extensiones no estándar para que sepa cuál es cuál.

SGI también tiene la documentación en línea (aunque es más antigua y, en algunos casos, no del todo precisa).

IBM tiene documentación similar disponible en su sitio web, y creo que GCC también lo hace.

+0

Gracias jalf que fue útil :) – AraK

+0

Oh, ni siquiera se dio cuenta de que esta era su pregunta. Pensé que ya sabías esto. ;) – jalf

-2

¿dónde puedo encontrar la documentación oficial de la biblioteca estándar?

El estándar de C++ está disponible en tapa dura, ISBN 0470846747. Borrador de Norma son ampliamente disponible en formato PDF, pero tendrás que tener cuidado para que coincida con las versiones oficiales (C++ 98,03, 11 o 14). Los borradores actuales superan el estándar C++ 14.

7

Lo mencioné en un comentario, pero después de pensarlo, parece ser una solución correcta. queue/stack/priority_queue (es decir, todas las clases de adaptadores) todos tienen un miembro protectedc que es el contenedor subyacente (consulte ISO/IEC 14882: 2003 sección 23.2.2.4), por lo que si hereda de alguno de estos, puede acceder directamente

Sé que la sabiduría típica es no heredar de contenedores STL debido a dtors no virtuales, pero este caso es una excepción. El objetivo no es sobrecargar la funcionalidad, sino hacer extensiones menores a la interfaz del adaptador. Aquí hay un ejemplo de cómo agregar la capacidad de acceder al contenedor subyacente.

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

Por desgracia, tendrá que recurrir a escribir-juegos de palabras para "actualizar" un std::queue<int> * existente a un tipo Adapter<std::queue<int> > * sin juego de palabras. Técnicamente, esto probablemente funcionar bien ... pero yo recomendaría contra:

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   

así que recomendaría el uso de typedef para que sea fácil de cambiar entre los dos. (También observe en mi ejemplo que solo necesita cambiar 1 línea para cambiarla de queue a stack debido al uso liberal de typedef s).

3

Basado en la accepted answer, un enfoque más general:

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

Como supe de this answer, .*& es en realidad dos operadores, donde el puntero resultante de &hack::c (que ha escriba ADAPTER::container_type ADAPTER::*) es el objetivo o el .* operador para recuperar el contenedor subyacente en sí. hack tiene acceso al miembro protegido, pero después de que se obtiene el puntero, se pierden las protecciones. Por lo tanto, a.*(&hack::c) está permitido.

0

Puede escribir una subclase para recuperar la variable miembro c. Ver este comentario de 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; 
Cuestiones relacionadas