En el manual de C++, 5.ª edición, el apéndice de soluciones ofrece un ejemplo sobre la declaración de switch :
case true: string file_name; // error: control bypasses an implicitly initialized variable. ... case false: if(file_name.empty()); // file_name is in scope but wasn't initialized.
Pero creo que está mal porque file_name se ha inicializado implícitamente y es por eso que el compilador marca ese error.
struct Foo{}; switch(val){ case 1: Foo f; // ok not-implicitly initialized like std::string break; case 2: f.some_member(); // use f; }Entonces, ¿son correctas mis conjeturas?
El término "inicialización implícita" suena técnico. Pero es un galimatías. No significa nada .
Y el compilador tampoco usa este término inventado. Esto es:
int main() { struct Foo { void some_member() {} int a = 1; }; int val = 0; switch (val){ case 1: Foo f; break; case 2: f.some_member(); // use f; } }Salida del compilador:
<source>: In function 'int main()': <source>:12:14: error: jump to case label 12 | case 2: | ^ <source>:10:17: note: crosses initialization of 'main()::Foo f' 10 | Foo f; | ^ Y eso es todo. Inicialización, simple y llanamente. Cuando elimina el inicializador para el miembro Foo::a , no hay más inicialización, y el compilador estará de acuerdo con eso:
int main() { struct Foo { void some_member() {} int a; }; int val = 0; switch (val){ case 1: Foo f; // OK - no initialization at all break; case 2: f.some_member(); // use f if (fa) {}; // undefined behavior, a is uninitialized here } } Ahora, fa no está inicializado, y está bien a menos que intente usar su valor. El acceso a fa no causa una falla en tiempo de compilación, pero es un comportamiento indefinido, como si escribiera lo siguiente:
int main() { int a; if (a) {} // undefined behavior, a isn't initialized }Los compiladores modernos pueden usar y usarán el acceso a variables/miembros no inicializados como una sugerencia de optimización. El código que utiliza el valor no inicializado puede eliminarse, por ejemplo.
De acuerdo, por ejemplo, con el estándar C++ 17 (Declaración 9.7)
3 Es posible transferir a un bloque, pero no de forma que pase por alto las declaraciones con la inicialización. Un programa que salta91 desde un punto donde una variable con duración de almacenamiento automático no está dentro del alcance hasta un punto donde sí lo está está mal formado a menos que la variable tenga un tipo escalar, un tipo de clase con un constructor predeterminado trivial y un destructor trivial, un versión calificada para cv de uno de estos tipos, o una matriz de uno de los tipos anteriores y se declara sin un inicializador
La clase std::string no tiene un constructor predeterminado trivial ni un destructor trivial.
En este fragmento de código
struct Foo{}; switch(val){ case 1: Foo f; // ok not-implicitly initialized like std::string break; case 2: f.some_member(); // use f; }la clase Foo tiene un constructor predeterminado trivial y un destructor trivial.
y por ejemplo (15.1 Constructores)
6 Un constructor predeterminado es trivial si no lo proporciona el usuario y si:
(6.1) — su clase no tiene funciones virtuales (13.3) ni clases base virtuales (13.1), y
(6.2) — ningún miembro de datos no estáticos de su clase tiene un inicializador de miembro predeterminado (12.2), y
(6.3) — todas las clases base directas de su clase tienen constructores predeterminados triviales, y
(6.4) — para todos los miembros de datos no estáticos de su clase que son del tipo de clase (o matriz de los mismos), cada clase tiene un constructor predeterminado trivial.
De lo contrario, el constructor predeterminado no es trivial.