2011-03-29 19 views
9

Supongamos que tengo una clase de C++, Container, que contiene algunos elementos del tipo Element. Por diversas razones, es ineficaz, indeseable, innecesario, poco práctico y/o imposible (1) modificar o reemplazar los contenidos después de la construcción. Algo a lo largo de las líneas de const std::list<const Element> (2).clase de contenedor C++ inmutable

Container puede cumplir muchos requisitos de los conceptos de "contenedor" y "secuencia" del STL. Puede proporcionar los diversos tipos como value_type, reference, etc. Puede proporcionar un constructor predeterminado, un constructor de copia, un tipo const_iterator, begin() const, end() const, size, empty, todos los operadores de comparación, y tal vez algunos de rbegin() const, rend() const, front() , back(), operator[]() y at().

Sin embargo, Container no pueden proporcionar insert, erase, clear, push_front, push_back, no const front, no const back, no const operator[], o no const at con la semántica esperados. Por lo tanto, parece que Container no puede calificar como una "secuencia". Además, Container no puede proporcionar operator=, y swap, y no puede proporcionar un tipo iterator que apunta a un elemento no const. Por lo tanto, ni siquiera puede calificar como un "contenedor".

¿Hay algún concepto de STL menos capaz que Container? ¿Hay un "contenedor de solo lectura" o un "contenedor inmutable"?

Si Container no cumple con ningún nivel definido de conformidad, ¿hay algún valor en conformidad parcial? ¿Es engañoso hacer que se vea como un "contenedor", cuando no califica? ¿Hay una forma concisa e inequívoca de que pueda documentar la conformidad para que no tenga que documentar explícitamente la semántica conforme? Y de manera similar, una forma de documentarlo para que los futuros usuarios sepan que pueden aprovechar el código genérico de solo lectura, pero no esperen que los algoritmos de mutación funcionen.

¿Qué obtengo si desactivo el problema para que Container se pueda asignar (pero sus elementos no son)? En ese punto, son posibles operator= y swap, pero la desreferenciación iterator aún devuelve const Element. ¿El Container ahora califica como un "contenedor"?

tiene aproximadamente la misma interfaz que Container. ¿Eso significa que no es ni un "contenedor" ni una "secuencia"?

Nota al pie (1) Tengo casos de uso que cubren todo este espectro. Tengo una clase de aspirante a contenedor que adapta algunos datos de solo lectura, por lo que debe ser inmutable. Tengo un aspirante a contenedor que genera sus propios contenidos según sea necesario, por lo que es mutable pero no puede reemplazar los elementos de la manera que requiere el STL. Todavía tengo otro aspirante a contenedor que almacena sus elementos de una manera que haría que insert() fuera tan lento que nunca sería útil. Y finalmente, tengo una cadena que almacena texto en UTF-8 mientras expongo una interfaz orientada al punto de código; una implementación mutable es posible pero completamente innecesaria.

Nota al pie (2) Esto es sólo para ilustración. Estoy bastante seguro de que std::list requiere un tipo de elemento asignable.

+0

¿Cómo se colocan los elementos iniciales en el contenedor? – fredoverflow

+0

@FredOverflow De manera diferente para cada uno de mis casos de uso. Para el adaptador, me dieron el contenedor adaptado ya poblado, y mi clase solo almacena un puntero a él. El contenedor autopoblado crea sus elementos a medida que se solicitan en base a algunos parámetros mutables. El contenedor donde la inserción genérica compatible con STL es demasiado lenta tiene métodos eficientes que no cumplen con STL para la inserción. Y a la cadena UTF-8 simplemente le asignan una matriz de bytes o un rango de iterador en su constructor. –

+0

Las soluciones obvias son usar iteradores, o moverlos de un contenedor mutable al 'contenedor 'inmóvil. –

Respuesta

1

Mientras su objeto pueda proporcionar un const_iterator conforme, no tiene que tener nada más.Debería ser bastante fácil implementar esto en su clase de contenedor.

(Si es aplicable, mirar a la biblioteca Boost.Iterators, sino que tiene iterator_facade y iterator_adaptor clases para ayudarle con los detalles esenciales)

2

El STL no define ningún conceptos menores; sobre todo porque la idea de const generalmente se expresa en un iterador o por nivel de referencia, no en un nivel por clase.

No debe proporcionar iterator con semántica inesperada, solo proporcione const_iterator. Esto permite que el código del cliente falle en el lugar más lógico (con el mensaje de error más legible) si se equivocan.

Posiblemente la forma más fácil de hacerlo sería encapsularlo y evitar todos los alias no const.

class example { 
    std::list<sometype> stuff; 
public: 
    void Process(...) { ... } 
    const std::list<sometype>& Results() { return stuff; } 
}; 

Ahora cualquier código de cliente sabe exactamente lo que pueden hacer con el valor de retorno de Results- nada que requiere mutación.