¿Es const_cast seguro?

92

No puedo encontrar mucha información sobre const_cast. La única información que pude encontrar (en Stack Overflow) es:

Se const_cast<>()utiliza para agregar / eliminar la const (ness) (o volatile-ness) de una variable.

Esto me pone nervioso. ¿El uso de un const_castcomportamiento podría causar un comportamiento inesperado? ¿Entonces qué?

Alternativamente, ¿cuándo está bien usarlo const_cast?

Mag Roader
fuente
4
La respuesta principal pasa por alto algo que podría ser horriblemente obvio, pero que vale la pena decirlo: solo se vuelve inseguro si intenta modificar un constobjeto original a través de una constreferencia / puntero de -ed. Si, en cambio, simplemente está tratando const_castde evitar una API especificada deficientemente (o, en mi caso, perezosa) que solo acepta una no constreferencia pero solo se usará en constmétodos ... no hay problema en absoluto.
underscore_d
1
@underscore_d: Una versión más precisa de la pregunta (y respuesta) que cubre eso es: ¿Está permitido descartar const en un objeto definido por const siempre que no esté realmente modificado?
Peter Cordes

Respuestas:

88

const_castes seguro solo si está lanzando una variable que originalmente no era const. Por ejemplo, si tiene una función que toma un parámetro de a const char *, y pasa un modificable char *, es seguro que const_castese parámetro vuelva a a char *y lo modifique. Sin embargo, si la variable original era de hecho const, el uso const_castresultará en un comportamiento indefinido.

void func(const char *param, size_t sz, bool modify)
{
    if(modify)
        strncpy(const_cast<char *>(param), sz, "new string");
    printf("param: %s\n", param);
}

...

char buffer[16];
const char *unmodifiable = "string constant";
func(buffer, sizeof(buffer), true);  // OK
func(unmodifiable, strlen(unmodifiable), false); // OK
func(unmodifiable, strlen(unmodifiable), true);  // UNDEFINED BEHAVIOR
Adam Rosenfield
fuente
9
No es verdad. Estándar C ++. §7.1.​5.1/4 says Except that any class member declared mutable (7.1.1) can be modified, any attempt to modify a const object during its lifetime (3.8) results in undefined behavior ¡Cualquier intento ! No hay palabras sobre la variable original.
Alexey Malistov
20
@Alexey: La variable original trata sobre lo que se apunta o se hace referencia. Puede tomar una referencia constante a un objeto no constante y, por lo tanto, convertirla en una referencia grabable es un comportamiento bien definido, ya que el objeto referido no es realmente constante.
Puppy
43
@Alexey Malistov: No. Un "objeto" se refiere a la región real de almacenamiento ocupada en la memoria (§1.7). Tomar una referencia constante a un objeto no constante no hace que el objeto sea constante. Solo en el caso de un parámetro de referencia constante ( no un parámetro de puntero constante) se permite al compilador hacer una copia silenciosamente (§5.2.2 / 5); Este no es el caso aquí.
Adam Rosenfield
8
"Sin embargo, si la variable original era de hecho const, entonces el uso de const_cast dará como resultado un comportamiento indefinido" Esta afirmación es falsa.
Lightness Races en órbita
7
Es no UB utilizar const_castpara eliminar constde algo que fue declarado inicialmente const. Pero es UB intentar escribir en ese objeto. Siempre que lea, está bien y el problema const_casten sí mismo no causa UB. Es una idea horrible, pero no es inherentemente UB.
Jesper Juhl
35

Puedo pensar en dos situaciones en las que const_cast es seguro y útil (puede haber otros casos válidos).

Una es cuando tiene una instancia, referencia o puntero constante, y desea pasar un puntero o referencia a una API que no es correcta const, pero está SEGURO de que no modificará el objeto. Puede const_cast el puntero y pasarlo a la API, confiando en que realmente no cambiará nada. Por ejemplo:

void log(char* text);   // Won't change text -- just const-incorrect

void my_func(const std::string& message)
{
    log(const_cast<char*>(&message.c_str()));
}

La otra es si está utilizando un compilador más antiguo que no implementa 'mutable', y desea crear una clase que sea lógicamente constante pero no bit a bit. Puede const_cast 'this' dentro de un método const y modificar miembros de su clase.

class MyClass
{
    char cached_data[10000]; // should be mutable
    bool cache_dirty;        // should also be mutable

  public:

    char getData(int index) const
    {
        if (cache_dirty)
        {
          MyClass* thisptr = const_cast<MyClass*>(this);
          update_cache(thisptr->cached_data);
        }
        return cached_data[index];
    }
};
Fred Larson
fuente
Esto ... no parece responder a esta pregunta. Preguntó si const_castpuede causar un comportamiento indefinido, no qué aplicaciones útiles son
Michael Mrozek
13
De la pregunta: "Alternativamente, ¿cuándo está bien usar const_cast?"
Fred Larson
Como en "cuando no es indefinido"; no busca ejemplos de cuándo es útil
Michael Mrozek
Solo podemos pegarnos en las letras de la pregunta. En base a esto, la presentación de un uso ilustrativo de const_castes una respuesta válida. No hay hepreguntas, ya que la pregunta es el tema.
eigenfield
24

Me cuesta creer que esa sea la única información que pueda encontrar sobre const_cast. Citando del segundo éxito de Google :

Si descarta la constness de un objeto que se ha declarado explícitamente como const e intenta modificarlo, los resultados no están definidos.

Sin embargo, si descarta la constness de un objeto que no se ha declarado explícitamente como const, puede modificarlo de forma segura.

Rob Kennedy
fuente
Grrrreaat respuesta, combine esto con esta respuesta y obtendrá la imagen completa.
bobobobo
hmm. con respecto a la segunda declaración en su respuesta, ¿puedo preguntarle cómo hay una "const" ness para un objeto que no se declaró explícitamente como const en primer lugar?
hAcKnRoCk
Hay muchas formas de hacer que un objeto no constante sea constante, @Iam. Por ejemplo, pase el objeto como parámetro de referencia constante. O asígnelo a un puntero a constante. O utilizar const_cast. O llame a un método const en él.
Rob Kennedy
12

Lo que dice Adam. Otro ejemplo en el que const_cast puede ser útil:

struct sample {
    T& getT() { 
        return const_cast<T&>(static_cast<const sample*>(this)->getT()); 
    }

    const T& getT() const { 
       /* possibly much code here */
       return t; 
    }

    T t;
};

Primero agregamos const al tipo thisapunta a, luego llamamos a la versión const de getT, y luego eliminamos const del tipo de retorno, que es válido ya que tdebe ser no constante (de lo contrario, la versión no constante de getTno podría tener ha sido llamado). Esto puede resultar muy útil si tiene un cuerpo de función grande y desea evitar el código redundante.

Johannes Schaub - litb
fuente
3
Prefiero usar un elenco estático para agregar constness: static_cast <const sample *> (this). Cuando leo const_cast, significa que el código está haciendo algo potencialmente peligroso, así que trato de evitar su uso cuando sea posible.
mfazekas
1
correcto, el primero puede ser static_cast, o incluso implícito_cast (de boost). Lo arreglaré usando yeso estático. gracias
Johannes Schaub - litb
3
Voy y vengo sobre si es mejor const_casto si static_castes mejor. const_castsolo puedes hacer lo que quieras: cambiar los calificadores cv. static_castpuede realizar 'silenciosamente' otras operaciones que no tiene la intención. Sin embargo, el primer yeso es completamente seguro y static_casttiende a ser más seguro que const_cast. Creo que esta es una situación en la que const_castcomunica mejor su intención, pero static_castcomunica mejor la seguridad de sus acciones.
David Stone
10

La respuesta corta es no, no es seguro.

La respuesta larga es que si sabe lo suficiente para usarlo, entonces debería ser seguro.

Cuando estás transmitiendo, lo que esencialmente estás diciendo es: "Sé algo que el compilador no sabe". En el caso de const_cast, lo que está diciendo es: "Aunque este método toma una referencia o un puntero no constante, sé que no cambiará el parámetro que le paso".

Entonces, si realmente sabe lo que dice saber al usar el yeso, entonces está bien usarlo.

JohnMcG
fuente
5

Está destruyendo cualquier posibilidad de seguridad de subprocesos, si comienza a modificar cosas que el compilador pensaba que eran constantes.

Matt Cruikshank
fuente
1
¿Qué? Si tiene objetos inmutables (const), puede compartirlos trivialmente entre hilos. En el instante en que una parte de su código desecha la consistencia, ¡pierde toda la seguridad de su hilo! ¿Por qué me han modificado para esto? suspiro
Matt Cruikshank
8
Const es ciertamente una herramienta útil para hacer que el código sea seguro para subprocesos, pero no ofrece garantías (excepto en el caso de las constantes de tiempo de compilación). Dos ejemplos: un objeto const puede tener miembros mutables, y tener un puntero constante a un objeto no dice nada sobre si el objeto en sí podría estar cambiando.
James Hopkin
Creo que esta es una buena respuesta porque no pensé en los sentimientos de confianza y seguridad del optimizador del compilador en su uso de la palabra const. constes la confianza. const_castestá rompiendo esa confianza :(
bobobobo
1
Con respecto a la seguridad de subprocesos y mutables: channel9.msdn.com/posts/…
MFH
-3
#include <iostream>
using namespace std;

void f(int* p) {
  cout << *p << endl;
}

int main(void) {
  const int a = 10;
  const int* b = &a;

  // Function f() expects int*, not const int*
  //   f(b);
  int* c = const_cast<int*>(b);
  f(c);

  // Lvalue is const
  //  *b = 20;

  // Undefined behavior
  //  *c = 30;

  int a1 = 40;
  const int* b1 = &a1;
  int* c1 = const_cast<int*>(b1);

  // Integer a1, the object referred to by c1, has
  // not been declared const
  *c1 = 50;

  return 0;
}

fuente: http://publib.boulder.ibm.com/infocenter/comphelp/v8v101/index.jsp?topic=%2Fcom.ibm.xlcpp8a.doc%2Flanguage%2Fref%2Fkeyword_const_cast.htm

Señor ángel
fuente
Votación en contra debido a un enlace de spam
eigenfield