c++ - managerç - tag assistant
¿Hay alguna forma de acceder al contenedor subyacente de los adaptadores de contenedor STL? (9)
¿Hay una forma estándar de acceder al contenedor subyacente de stack
, queue
, priority_queue
?
Encontré un método llamado: _Get_container()
en la implementación de la stack
y la queue
VS2008
, ¡pero nadie para la priority_queue
! Creo que no es estándar de todos modos.
Además, sé que es una pregunta tonta! ¿Dónde puedo encontrar documentación oficial de la biblioteca estándar?
Solo para aclarar, no estaba intentando desordenar el contenedor subyacente. Todo lo que estaba tratando de hacer es esto:
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 veas que _Get_container()
no es estándar, y que no hay nadie para priority_queue
en la implementación de VS2008
.
Basado en la respuesta aceptada , 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 aprendí de esta respuesta,. .*&
Son en realidad dos operadores, donde el puntero resultante de &hack::c
(que tiene el tipo ADAPTER::container_type ADAPTER::*
) es el objetivo o el operador .*
Para recuperar el contenedor subyacente sí mismo. hack
tiene acceso al miembro protegido, pero después de obtener el puntero, se pierden las protecciones. Así que a.*(&hack::c)
está permitido.
Ciertamente espero que no haya una manera de acceder al contenedor subyacente de una cola de prioridad. Si pudiera, entonces podría desordenar la estructura del montón interno 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 la cola y abstraen todo lo demás. Por lo tanto, si necesitaba usar otras características, 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 de SGI here . Existen algunas diferencias entre la STL de SGI y la del estándar C ++, pero en su mayoría se mencionan en ese sitio. Además, cppreference.com es una documentación wiki de la biblioteca de C ++ que se está volviendo más completa.
Como regla general, cualquier identificador que comience con un guión bajo es una extensión específica del proveedor o un detalle de implementación. Así que _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 lenguaje estándar. Como dijo Neil Butterworth, hay copias en borrador disponibles de forma gratuita en línea (que siguen siendo muy útiles. Las diferencias entre ellas y la versión final son mínimas). Alternativamente, puedes comprar una copia. Debería estar disponible desde cualquier organización que represente a ISO en su país (y probablemente también de otras miles de 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 se encuentra con la versión de 1998, también puede usar eso. Las diferencias son realmente ridículas entre las dos, y básicamente son solo algunas aclaraciones. Es Probablemente sea mejor mantenerse alejado de los borradores de C ++ 0x, ya que los cambios son mucho más extensos)
Aparte de eso, se requiere que cada implementación de la biblioteca estándar documente una gran cantidad de detalles (comportamiento definido por la implementación, cosas que no están especificadas en el estándar, pero 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 es del todo precisa).
IBM tiene documentación similar disponible en su sitio web, y creo que GCC también.
Lo mencioné en un comentario, pero después de pensar que parece ser una solución aceptable. queue
/ stack
/ priority_queue
(es decir, todas las clases de adaptador) tienen un miembro protected
c
que es el contenedor subyacente (consulte ISO / IEC 14882: 2003, sección 23.2.2.4), por lo que si hereda de cualquiera de estos, Puede acceder a él directamente.
Sé que la sabiduría típica es no heredar de los contenedores STL debido a controladores 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;
}
}
Desafortunadamente, tendrá que recurrir al tipo punning para "actualizar" un std::queue<int> *
a un Adapter<std::queue<int> > *
sin el tipo punning. Técnicamente, es probable que esto funcione bien ... pero lo recomiendo:
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 typedefs para facilitar el intercambio entre los dos. (También note en mi ejemplo que solo necesita cambiar 1 línea para cambiarlo de una queue
a una stack
debido al uso liberal de typedef
s).
No, no hay una manera estándar de hacerlo. En cuanto al acceso al estándar, no está disponible en la web, ¡usted tiene que comprar una copia! Sin embargo, hay varias copias de borradores disponibles aquí .
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;
Vi la siguiente solución en algún lugar de la web y la 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;
}
Que te diviertas :).
Esta página de SGI es la documentación más "oficial" disponible en línea, creo.
La razón por la que no puede obtener acceso directo al contenedor subyacente es que el adaptador cambia el patrón de uso, y tener disponibles los métodos del contenedor subyacente violaría ese nuevo patrón de uso. Por ejemplo:
2 Esta restricción es la única razón para que exista una cola. Cualquier contenedor que sea tanto una secuencia de inserción frontal como una secuencia de inserción trasera se puede usar 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 de contenedor en lugar de la deque del contenedor es dejar en claro que está realizando solo operaciones de cola, y ninguna otra operaciones 2
Si quieres evitar esta característica de diseño, puedes hacer algo como:
template <typename T>
class clearable_queue : public std::queue<T>
{
public:
void clear() { c.clear(); }
};
¿Dónde puedo encontrar documentación oficial de la biblioteca estándar?
El estándar de C ++ está disponible en tapa dura, ISBN 0470846747. Los borradores del estándar están ampliamente disponibles en formato PDF, pero debe tener cuidado de hacerlos coincidir con las versiones oficiales (C ++ 98,03,11 o 14). Los borradores actuales superan el estándar C ++ 14.