Tengo un código que se parece a esto:

void addPtrs(vector<Thing*> & ThingPtrs, const vector<Thing> & actualThings) {
       for (const Thing & t : actualThings)
           if (/*some condition*/)
               ThingPtrs.push_back(&t);
}

Básicamente, quiero modificar ThingPtrs dándole punteros a Thing que tienen alguna condición. Sin embargo, debido a que no quiero modificar el Thing real, etiqueté actualThings como const, y ahora no puedo asignar un puntero const a un puntero que no sea const puntero.

Obviamente, una solución es simplemente no hacer actualThings const, pero en realidad no pretendo modificarlo. Otra solución es usar un const_cast, pero también me gustaría evitar eso si es posible.

¿Cuáles son mis opciones en una situación como esta?

0
Dillydill123 12 ene. 2017 a las 05:22
Si nunca tiene la intención de modificar ningún Thing, su ThingPtrs se definiría como std::vector<Thing const *>&. Al ver que almacena punteros que no son const, implica que se requerirá alguna modificación en algún momento, por lo que no desea ninguna const ness.
 – 
Algirdas Preidžius
12 ene. 2017 a las 05:34
Tengo la intención de modificar Thing s, pero solo a través de actualThings en lugar de ThingPtrs. ¿Puedo definir ThingPtrs como de tipo vector<const Thing*>?
 – 
Dillydill123
12 ene. 2017 a las 06:28
Sí, si cambia ThingPtrs a vector<const Thing*> entonces su función debería compilarse bien.
 – 
François Andrieux
12 ene. 2017 a las 06:38

1 respuesta

La mejor respuesta

Por diseño, const std::vector no le permitirá modificar sus elementos directamente. Dado que actualThings es constante, no puede modificar su contenido. Cualquier referencia o puntero a sus elementos será constante.

Cualquier solución que encuentre sin cambiar el prototipo de la función finalmente implicará convertir un const Thing & o const Thing * en su equivalente no constante, independientemente de cómo lo vista. Cualquier lanzamiento de puntero o reinterpretación sería simplemente un const_cast redondo.

Si le preocupa que addPtrs no modifique actualThings (en el sentido de llamar a su miembro no constante), puede cambiar su interfaz para aceptar un par de iteradores de inicio / fin. Otros cambios que permitirán que su función se compile incluyen eliminar const de actualThings para obtener vector<Thing> & actualThings o agregar const al tipo de elementos en ThingPtrs para obtener vector<const Thing*> & ThingPtrs

2
François Andrieux 12 ene. 2017 a las 06:37
Así que probé vector<const Thing*> y me sorprende que esté compilado. ¿Cómo se permite esto, dado que cuando un vector alcanza su capacidad, tiene que rehacer su contenedor? También noté que vector<Thing * const> no se compila.
 – 
Dillydill123
12 ene. 2017 a las 06:53
1
Este enfoque no protege contra la invalidación. De hecho, se romperá si / cuando actualThings cambie de tamaño. Todos los punteros y referencias a elementos, junto con todos los iteradores, se invalidan cuando eso sucede. Un puntero constante no garantiza que el objeto señalado no pueda cambiar , solo que no puede cambiarlo usando el puntero. Para su otra pregunta, consulte esta pregunta para obtener más información sobre los diferentes lugares en los que puede poner const en un tipo de puntero.
 – 
François Andrieux
12 ene. 2017 a las 07:06
Para su segunda pregunta en el comentario, solo para asegurarse de que conoce la diferencia entre const X* / X const * y X * const, ¿verdad? (puntero a const X vs puntero const a X)
 – 
Adrian Shum
12 ene. 2017 a las 07:09
¡Sí, lo hago! Creo que entiendo por qué no podemos hacer que vector<int * const> me corrija si me equivoco, pero no se compila porque un puntero constante tendría que inicializarse cuando se crea, lo que no sería el caso si vive dentro del vector. ¡Ahora esto me da otra pregunta! ¿Podríamos hacer map ? ¡Parece que debería funcionar!
 – 
Dillydill123
12 ene. 2017 a las 07:20
int * const es un puntero a un int mutable (no constante) y no se puede asignar a una nueva dirección una vez inicializado. Todavía tiene el mismo problema que vector<int*> donde no puede asignarle un const int *. También tiene un problema nuevo porque std :: vector requiere que T sea CopyAssignable.
 – 
François Andrieux
12 ene. 2017 a las 07:28