¿C ++ 11 permite el vector <const T>?

81

Los requisitos del contenedor han cambiado de C ++ 03 a C ++ 11. Mientras que C ++ 03 tenía requisitos generales (p. Ej., Copia de construcción y asignabilidad para vector), C ++ 11 define requisitos detallados en cada operación de contenedor (sección 23.2).

Como resultado, puede, por ejemplo, almacenar un tipo que es copia-construible pero no asignable, como una estructura con un miembro constante, en un vector siempre que solo realice ciertas operaciones que no requieren asignación (construcción y push_backson tales operaciones ; insertno es).

Lo que me pregunto es: ¿significa esto que el estándar ahora lo permite vector<const T>? No veo ninguna razón por la que no debería const T, al igual que una estructura con un miembro constante, es un tipo que es copia construible pero no asignable, pero puede que me haya perdido algo.

(Parte de lo que me hace pensar que me he perdido algo es que el tronco gcc se bloquea y se quema si intenta crear una instancia vector<const T>, pero está bien vector<T>si T tiene un miembro constante).

HighCommander4
fuente

Respuestas:

54

No, creo que los requisitos del asignador dicen que T puede ser un "tipo de objeto no constante, sin referencia".

No podría hacer mucho con un vector de objetos constantes. Y a const vector<T>sería casi lo mismo de todos modos.


Muchos años después, esta respuesta rápida y sucia todavía parece estar atrayendo comentarios y votos. No siempre despierto. :-)

Entonces, para agregar algunas referencias adecuadas:

Para el estándar C ++ 03, que tengo en papel, la Tabla 31 en la sección [lib.allocator.requirements] dice:

T, U any type

No es que ningún tipo funcione realmente.

Entonces, el siguiente estándar, C ++ 11, dice en un borrador detallado en [allocator.requirements] y ahora en la Tabla 27:

T, U, C any non-const, non-reference object type

que está muy cerca de lo que escribí originalmente de memoria. De esto también se trataba la pregunta.

Sin embargo, en C ++ 14 ( borrador N4296 ), la Tabla 27 ahora dice:

T, U, C any non-const object type

Posiblemente porque una referencia tal vez no sea un tipo de objeto después de todo.

Y ahora en C ++ 17 ( borrador N4659 ) es la Tabla 30 que dice:

T, U, C any cv-unqualified object type (6.9)

Así que no solo está constdescartado, sino también volatile. Probablemente noticias viejas de todos modos, y solo una aclaración.


Consulte también la información de primera mano de Howard Hinnant , actualmente a continuación.

Bo Persson
fuente
42
En pocas palabras: No diseñamos contenedores para contener const T. Aunque lo pensé un poco. Y estuvimos muy cerca de hacerlo por accidente. Hasta donde yo sé, el problema actual es el par de addressfunciones miembro sobrecargadas en el asignador predeterminado: cuando T es constante, estas dos sobrecargas tienen la misma firma. Una forma fácil de corregir esto sería especializarse std::allocator<const T>y eliminar una de las sobrecargas.
Howard Hinnant
4
@ HighCommander4: No estoy seguro. En libc ++ puedo construir un vector (incluso uno no vacío) con un asignador cooperativo. No puedo hacer nada más (no constante) con él. No estoy seguro de si eso se ajusta a su definición de "funciona". Tampoco estoy seguro si sin darme cuenta estoy aprovechando una extensión. Para estar seguro, necesitaría invertir mucho más tiempo en esta pregunta. Hice una gran inversión de tiempo antes, pero eso fue hace varios años y muchas cosas han cambiado en el ínterin. Si funciona, no es una decisión del comité.
Howard Hinnant
1
@Howard: No puedo pensar en ningún obstáculo técnico para poder hacerlo push_back. Pero si el diseño no lo permite, es mejor que no lo hagamos. Solo tenía curiosidad.
HighCommander4
7
Un caché es a menudo un contenedor mutable de objetos inmutables, y un vector ordenado es a menudo una alternativa a un mapa, así que no estoy de acuerdo en que un vector de objetos constantes sea de poca utilidad.
Chris Oldwood
9
Es una pena. Estaba usando std::vector<const T>exactamente porque es muy similar a const std::vector<T>, pero sin las implicaciones negativas de este último para la clase que lo tiene. De hecho, std::vector<const T>es EXACTAMENTE lo que necesito semánticamente en la mayoría de los casos en los que uso vector. Ahora tengo que dejarlo const, junto con la fiabilidad que proporciona.
Jirafa violeta
29

Actualizar

Bajo la respuesta aceptada (y correcta) comenté en 2011:

En pocas palabras: no diseñamos contenedores para contener const T. Aunque lo pensé un poco. Y estuvimos muy cerca de hacerlo por accidente. Hasta donde yo sé, el problema actual es el par de addressfunciones miembro sobrecargadas en el asignador predeterminado: cuando Tes const, estas dos sobrecargas tienen la misma firma. Una forma fácil de corregir esto sería especializarse std::allocator<const T>y eliminar una de las sobrecargas.

Con el próximo borrador de C ++ 17 me parece que ahora lo hemos legalizado vector<const T>, y también creo que lo hemos hecho accidentalmente . :-)

P0174R0 elimina las addresssobrecargas std::allocator<T>. P0174R0 no menciona el apoyo std::allocator<const T>como parte de su justificación.

Corrección

En los comentarios a continuación, TC señala correctamente que las addresssobrecargas están obsoletas , no eliminadas. Culpa mía. Los miembros obsoletos no aparecen en 20.10.9 donde std::allocatorse define, sino que son relegados a la sección D.9. Me olvidé de escanear el Capítulo D en busca de esta posibilidad cuando publiqué esto.

Gracias TC por la corrección. Contemplé eliminar esta respuesta engañosa, pero tal vez sea mejor dejarlo con esta corrección para que tal vez evite que alguien más lea mal la especificación de la misma manera que yo.

Howard Hinnant
fuente
6
¡Eso es muy divertido! (Ahora solo tenemos que estar realmente callados al respecto y dejar que se deslice en C ++ 17 sin que nadie se dé cuenta :))
HighCommander4
¿ La tabla de requisitos del asignador todavía no la prohíbe por completo? En cualquier caso, P0174R2 (que es la revisión votó a) sólo se desaprueba, no se elimina, address.
TC
@TC: Tienes toda la razón. Gracias por la corrección.
Howard Hinnant
Entonces, c ++ 2x finalmente permitirá vector<const T>:)
MM
1
La respuesta "En pocas palabras: no diseñamos contenedores para contener const T" asume que el objetivo es que el contenedor debe contener "const T". Sin embargo, se podría argumentar que el objetivo del usuario es restringir las operaciones en el contenedor, de modo que, por ejemplo, 'back ()' devuelva "const T &", independientemente de lo que contenga el contenedor.
Hans Olsson
8

Aunque ya tenemos muy buenas respuestas sobre esto, decidí contribuir con una respuesta más práctica para mostrar qué se puede y qué no se puede hacer.

Entonces esto no funciona:

vector<const T> vec; 

Simplemente lea las otras respuestas para comprender por qué. Y, como habrás adivinado, esto tampoco funcionará:

vector<const shared_ptr<T>> vec;

Tya no es const, pero vectortiene shared_ptrs, no Ts.

Por otro lado, esto hace el trabajo:

vector<const T *> vec;
vector<T const *> vec;  // the same as above

Pero en este caso, const es el objeto al que se apunta, no el puntero en sí (que es lo que almacena el vector). Esto sería equivalente a:

vector<shared_ptr<const T>> vec;

Lo cual está bien.

Pero si ponemos constal final de la expresión, ahora convierte el puntero en a const, por lo que lo siguiente no se compilará:

vector<T * const> vec;

Un poco confuso, estoy de acuerdo, pero te acostumbras.

Lucio Paiva
fuente
4

Complementando las otras respuestas, otro enfoque es utilizar:

vector<unique_ptr<const T>> vec;

Si es el caso en el que desea hacer valer que solo vectiene la propiedad de sus elementos. O si desea una dinámica de mover elementos vecy, en algún momento, moverlos.

Como a cabo en punta, puntero constsemántica puede ser confuso, pero shared_ptry unique_ptrno lo son. const unique_ptr<T>es un puntero constante y unique_ptr<const T>es un puntero constante como cabría esperar.

Daniel Gouvêa
fuente