2012-01-05 11 views
13

C++ 11 §12.1/14:Construcción de objeto const

Durante la construcción de un objeto const, si se accede al valor del objeto o cualquiera de sus subobjetos a través de un lvalue que no es obtenido, directa o indirectamente, del constructor este puntero, el valor del objeto o subobjeto así obtenido es no especificado. [Ejemplo:

struct C; 
void no_opt(C*); 

struct C { 
    int c; 
    C() : c(0) { no_opt(this); } 
}; 

const C cobj; 

void no_opt(C* cptr) { 
    // value of cobj.c is unspecified 
    int i = cobj.c * 100; 
    cptr->c = 1; 
    // value of cobj.c is unspecified 
    cout << cobj.c * 100 << '\n'; 
} 

Compilar el ejemplo anterior da salida 100. Mi pregunta es por qué el valor de cobj.c no debe especificarse cuando la lista de inicialización lo establece en 0 antes de ingresar al constructor. ¿Cómo es este comportamiento diferente en caso de que se use un objeto no const?

+0

Me gustaría señalar que en el ejemplo anterior 'cobj.Se accede a c' antes de que 'cobj' esté completamente construido. Me parece que incluso para objetos no const esta acción es dudosa. –

+0

@MatthieuM. ¿Por qué? Me parece válido (incluso si agrega una clase base a la estructura C). –

+0

@VJovic: siempre que el constructor no se haya ejecutado, el objeto aún no está activo; consulte la visión de Sutter en [Fallas del constructor (o, los objetos que nunca fueron)] (http://www.gotw.ca/publications) /mill13.htm). Si un objeto aún no está vivo, acceder a él es dudoso. No digo que sea necesariamente indefinido o no especificado, solo que "huele mal". –

Respuesta

6

Genuinamente const los objetos pueden ser tratados por el compilador como constantes legítimas. Puede suponer que sus valores nunca cambian o incluso almacenarlos en la memoria const, p. ROM o Flash. Por lo tanto, debe usar la ruta de acceso no const proporcionada por this, siempre que el objeto no sea constante. Esta condición solo existe durante la construcción y destrucción del objeto.

Offhand, creo que no es necesario que haya un requisito correspondiente para los destructores porque la duración del objeto ya ha finalizado y cobj.c es inaccesible tan pronto como comienza el destructor para cobj.

Como menciona Matthieu, es un fuerte "olor a código" acceder a un objeto además de this durante la construcción o destrucción. Al revisar C++ 11 §3.8 [basic.life] ¶1 y 6, parece que cobj.c dentro del constructor es UB por la misma razón que está dentro del destructor, independientemente de que el objeto sea const o §12.1/14, porque su duración no comienza hasta que se completa la inicialización (el constructor regresa).

Es probable que funcione, pero sonará las alarmas para los buenos programadores de C++, y según el libro es ilegal.

+0

'C(): c (0) {c = 1; } '- ¿entonces lo que estás diciendo es que esto tampoco es válido? –

+0

Parece fuera de tema: no aborda el hecho de que 'cobj.c' tiene un valor no especificado durante la ejecución del constructor' C' (incluso después de ejecutar la lista de inicialización). Tampoco aborda por qué el comportamiento sería diferente (en este sentido) entre los objetos const y non const. –

+0

@MatthieuM. Creo que he cubierto ambos problemas. El problema es que el compilador puede reconocer que la ruta de acceso 'cobj.c' tiene un valor que no puede cambiar. No importa qué función acceda, un objeto 'const' que no tenga un valor constante está fuera de los límites. – Potatoswatter

3

El motivo de la regla citada es permitir al compilador realizar optimizaciones en función de la constidad del objeto. Por ejemplo, dependiendo de la optimización, su compilador podría reemplazar el segundo cobj.c * 100 en no_opt con i. Lo más probable es que en este caso particular , el optimizador suprima por completo el i y su inicialización , por lo que el código parecerá funcionar. Pero este podría no ser el caso si también muestra i, antes de cambiar cptr->c; todo depende de lo agresivo que el compilador optimiza. Sin embargo, el compilador se le permite asumir que *cptr no es un alias para cobj, porque cobj es un objeto const, donde al modificar a través *cptr, por lo que no puede apuntar a un objeto constante sin un comportamiento indefinido.

Si el objeto no es const, por supuesto, el problema no ocurre; el compilador siempre debe tener en cuenta un posible alias entre *cptr y cobj.

Cuestiones relacionadas