2012-06-25 24 views
25

Duplicar posible:
int var = 1; void main() { int i = i; }¿Por qué 'int i = i;' ¿legal?

El siguiente código puede pasar compilar bajo tanto g ++ y Visual C++. ¿Por qué es legal? Parece irrazonable y puede causar errores ocultos.

int main() { 
    int i = i; 
} 
+7

Para mí no es ilegal, es solo un abuso de notación. –

+8

Se evalúa como (i) 'int i' (ii)' i = i' en ese orden –

+0

Creo que por la misma razón que solo 'int i;' sin asignar 'i' es legal. – asmeurer

Respuesta

42

EDIT: Es sintácticamente, pero da como resultado un comportamiento indefinido si se utilizan x.

No es no legal porque está asignando una variable no inicializada con otra (bueno, la misma) variable no inicializada. Solo porque compila no significa que sea legal. Es una sintaxis válida de C++, sí, pero no es legal.

El lado derecho del operador de asignación debe evaluarse completamente en el momento de la asignación. En este caso, eso es i, que no está inicializado.

Créditos a Steve Jessop, quien desenterró la cita:

4,1/1, la conversión-valor-i-a rvalue

[...] si el objeto no está inicializado, un programa que necesita esta conversión tiene un comportamiento indefinido.

+0

Ese argumento es circular, tener una variable cuyo valor no está definido ciertamente no es ilegal. Compárelo simplemente con 'int i;' que también deja 'i' con un valor indefinido. – unwind

+0

@nhahtdh eso no significa que sea legal. –

+0

Luchian, compila y no hay nada en la especificación que lo desautorice. – OmnipotentEntity

7

Puede dejar g++ advertirle sobre este caso de uso con -Winit-self (en conjunción con -Wuninitialized), y si usted trata a las advertencias como errores, debe satisfacer su picor.

Esta técnica de usar el constructor de copia para autoiniciar se usa a veces para suprimir la ejecución de un constructor/inicializador predeterminado de un objeto global. Esto puede ser necesario si el constructor predeterminado del objeto global es solo 0 inicializar el objeto, pero el objeto se usó antes de que el constructor se hubiera ejecutado. Como retroceso a C, las variables globales son 0 inicializadas al inicio del programa, y ​​luego el tiempo de ejecución de C++ comienza a ejecutar constructores globales. Para aquellos casos angostos en los que el constructor definido que se habría ejecutado solo fuera a 0 del objeto, la autoinicialización no causa ningún daño.

En el caso general, la autoinicialización del constructor de copias es una mala práctica, ya que generalmente causaría el mismo tipo de problemas que el uso de una variable no inicializada (es decir, un comportamiento indefinido). En el ejemplo particular en la pregunta del OP, i es local en main, y por lo tanto no está inicializado. El resultado de leer una variable no inicializada es siempre un comportamiento indefinido.

+0

Parece que no genera ninguna advertencia con '-Winit-self' para mí. – nhahtdh

+0

@nhahtdh: Gracias, actualicé la publicación. Saludos – jxh

+0

_Si_ el objeto tiene un tipo definido por el usuario, y el constructor toma la instancia por referencia, y nunca la desreferencia (en el constructor), entonces el código es legal. (El constructor definido por el usuario podría guardar la dirección, por ejemplo.) –

14

La razón es permitido por la sintaxis es que hay algunos casos raros en la que podría concebiblemente desea utilizar una variable puntero o referencia en su propia inicializador:

struct ThingManager { 
    void *thing; 
    ThingManager(void *thing) : thing(thing) {} 
    void Speak() { 
     if (thing == (void*)this) { 
      std::cout << "I'm managing myself\n"; 
     } else { 
      std::cout << "I'm managing " << thing << "\n"; 
     } 
    } 
}; 

ThingManager self_manager(&self_manager); 
ThingManager other_manager(&self_manager); 

Así C++ permite hacer referencia a una objeto en su propia expresión de inicializador (su nombre está en el alcance).Entonces, como siempre en C++, es su problema asegurarse de que no utiliza realmente un valor no inicializado (su ejemplo, int i = i; utiliza un valor no inicializado).

Su compilador puede ayudar a identificar usos de valores no inicializados, pero el estándar no lo requiere.

2

Puede usar cualquier variable declarada previamente como un inicializador de otra variable.

En este caso, tan pronto como el compilador analiza int i lo agrega a la tabla de símbolos, de modo que cuando ve el inicializador = i, el símbolo se puede resolver a partir de la declaración anterior.

No es un error porque el compilador puede darle sentido ya que puede generar código que hace exactamente lo que especifica el código fuente, siete si es semánticamente sospechoso. La filosofía de C y C++ es compilar cualquier cosa que pueda compilarse sintácticamente. Los errores semánticos generalmente solo emiten advertencias, y solo entonces si tales advertencias están habilitadas.

Cuestiones relacionadas