subvirtiendo subvertir subversivo subversion sinonimo significado palabras ejemplo derivadas conjugacion c++ const const-correctness aliasing

c++ - subversivo - subvertir sinonimo



¿Este código subvierte el sistema de tipo C++? (5)

Entiendo que tener un método const en C ++ significa que un objeto es de solo lectura a través de ese método, pero que todavía puede cambiar de otra manera.

Sin embargo, este código aparentemente cambia un objeto a través de una referencia const (es decir, a través de un método const ).

¿Es este código legal en C ++?

Si es así: ¿Está rompiendo la constancia del sistema de tipos? ¿Por qué por qué no?

¿Si no, porque no?

Nota 1: He editado el ejemplo un poco, por lo que las respuestas pueden estar refiriéndose a ejemplos anteriores.

Edición 2: al parecer, ni siquiera necesitas C ++ 11, así que eliminé esa dependencia.

#include <iostream> using namespace std; struct DoBadThings { int *p; void oops() const { ++*p; } }; struct BreakConst { int n; DoBadThings bad; BreakConst() { n = 0; bad.p = &n; } void oops() const { bad.oops(); } // can''t change itself... or can it? }; int main() { const BreakConst bc; cout << bc.n << endl; // 0 bc.oops(); // O:) cout << bc.n << endl; // 1 return 0; }

Actualizar:

He migrado la lambda a la lista de inicialización del constructor, ya que al hacerlo me permite decir subsiguientemente const BreakConst bc; , lo que, dado que bc es ahora constante (en lugar de simplemente el puntero), parece implicar ( por Stroustrup ) que la modificación de bc después de la construcción debería dar como resultado un comportamiento indefinido, incluso aunque el constructor y el llamador tuvieran No hay forma de saber esto sin ver las definiciones de los demás.


El método oops () no tiene permitido cambiar la constancia del objeto. Además no lo hace. Es su función anónima que lo hace. Esta función anónima no se encuentra en el contexto del objeto, sino en el contexto del método main () que tiene permiso para modificar el objeto.

Su función anónima no cambia este puntero de oops () (que se define como const y, por lo tanto, no puede cambiarse) y tampoco deriva de este modo alguna una variable no constante desde este puntero. Sí mismo no tiene ningún este puntero. Simplemente ignora este puntero y cambia la variable bc del contexto principal (que se pasa como un parámetro para su cierre). Esta variable no es constante y por lo tanto puede ser cambiada. También puede pasar cualquier función anónima cambiando un objeto completamente no relacionado. Esta función no sabe, que está cambiando el objeto que la almacena.

Si lo declararas como

const BreakConst bc = ...

entonces la función principal también lo manejaría como objeto constante y no podría cambiarlo.

Edición: En otras palabras: el atributo const está vinculado al valor-l concreto (referencia) que accede al objeto. No está ligado al objeto en sí.


El problema es uno de constantes lógicos contra constantes a nivel de bits. El compilador no sabe nada sobre el significado lógico de su programa, y ​​solo aplica constantes a nivel de bits. Depende de usted implementar la lógica. Esto significa que en casos como el que se muestra, si la memoria apuntada es parte lógica del objeto, debe abstenerse de modificarlo en una función constante, incluso si el compilador lo permite (ya que no es parte de la imagen a nivel de bits del objeto). Esto también puede significar que si parte de la imagen bitwise del objeto no es parte del valor lógico del objeto (por ejemplo, un recuento de referencia incrustado, o valores almacenados en caché), lo hace mutable , o incluso descartado const, en los casos donde lo modifique sin modificar el valor lógico del objeto.


La característica const simplemente ayuda contra el mal uso accidental. No está diseñado para evitar la piratería de software dedicado. Es lo mismo que la membresía privada y protegida, alguien siempre puede tomar la dirección del objeto e incrementarlo a lo largo de la memoria para acceder a los elementos internos de la clase, no hay forma de detenerlo.

Así que, sí puedes moverte por la const. Si no hay nada más, simplemente puede cambiar el objeto en el nivel de memoria, pero esto no significa que const se haya roto.


Su código es correcto, porque no usa la referencia constante para modificar el objeto . La función lambda usa una referencia completamente diferente, que simplemente apunta al mismo objeto.

En general, tales casos no subvierten el sistema de tipos, porque el sistema de tipos en C ++ no garantiza formalmente que no puede modificar el objeto const o la referencia const. Sin embargo, la modificación del objeto const es el comportamiento indefinido.

Desde [7.1.6.1] los calificadores cv :

Un puntero o referencia a un tipo cv-cali fi cado no necesita apuntar o referirse a un objeto cv-cali fi cado, pero se trata como si lo hiciera; una ruta de acceso constante no puede usarse para modificar un objeto incluso si el objeto al que se hace referencia no es un objeto constante y se puede modificar a través de otra ruta de acceso.

Excepto que cualquier miembro de la clase declarado mutable (7.1.1) puede modificarse, cualquier intento de modificar un objeto const durante su vida útil (3.8) da como resultado un comportamiento no definido.


Ya vi algo similar. Básicamente, invoca una función de costo que invoca otra cosa que modifica el objeto sin saberlo.

Considera esto también:

#include <iostream> using namespace std; class B; class A { friend class B; B* pb; int val; public: A(B& b); void callinc() const; friend ostream& operator<<(ostream& s, const A& a) { return s << "A value is " << a.val; } }; class B { friend class A; A* pa; public: void incval() const { ++pa->val; } }; inline A::A(B& b) :pb(&b), val() { pb->pa = this; } inline void A::callinc() const { pb->incval(); } int main() { B b; const A a(b); // EDIT: WAS `A a(b)` cout << a << endl; a.callinc(); cout << a << endl; }

Esto no es C ++ 11, pero hace lo mismo: el punto es que const no es transitivo .

callinc() no cambia a sí mismo e incval no cambia b . Tenga en cuenta que en main puede incluso declarar const A a(b); en lugar de A a(b); Y todo compila lo mismo.

Esto funciona desde hace décadas, y en su muestra solo está haciendo lo mismo: simplemente reemplazó la clase B con un lambda.

EDITAR

Se modificó el main () para reflejar el comentario.