7

Estoy trabajando en una aplicación creada con VC9 y he visto una advertencia que no entiendo completamente: ¿por qué hay un aviso de "código inalcanzable" en la llave de cierre del ¿constructor?Código inalcanzable en la llave de cierre del constructor

El caso de prueba mínima para reproducir el problema es:

__declspec(noreturn) void foo() { 
    // Do something, then terminate the program 
} 
struct A { 
    A() { 
    foo(); 
    } // d:\foo.cpp(7) : warning C4702: unreachable code 
}; 
int main() { 
    A a; 
} 

Esto se debe compilar con/W4 para activar la alerta. Alternativamente, puede compilar con/we4702 para forzar un error en la detección de esta advertencia.

d:\>cl /c /W4 foo.cpp 
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64 
Copyright (C) Microsoft Corporation. All rights reserved. 

foo.cpp 
d:\foo.cpp(7) : warning C4702: unreachable code 

Puede alguien explicar lo que, precisamente, es inalcanzable aquí? Mi mejor teoría es que es el destructor, pero me gustaría obtener una respuesta definitiva.

Si quiero hacer que este código advierta, ¿cómo puedo lograrlo? Lo mejor que se me ocurre es convertir esto a un error en tiempo de compilación.

struct A { 
private: 
    A(); // No, you can't construct this! 
}; 
int main() { 
    A a; 
} 

Editar: de aclaración, que termina el programa con una función NoReturn normalmente no causa un código inalcanzable advertencia en la llave de cierre que encierra dicha llamada de función.

__declspec(noreturn) void foo() { 
    // Do something, then terminate the program 
} 
struct A { 
    A() { 
    } 
    ~A() { 
    foo(); 
    } 
}; 
int main() { 
    A a; 
} 

Resultados en:

d:\>cl /c /W4 foo3.cpp 
Microsoft (R) C/C++ Optimizing Compiler Version 15.00.21022.08 for x64 
Copyright (C) Microsoft Corporation. All rights reserved. 

foo3.cpp 
+7

Porque termina el programa en 'foo()'? – juanchopanza

+0

Hacer algo 'privado' y no implementarlo es la forma habitual, ** C++ 11 ** tiene '= eliminar' para lograr eso. –

+1

debe 'foo()' ser '__declspec (noreturn)'? – ixe013

Respuesta

3

Gorpik está en el camino correcto. Creé dos casos de prueba similares, los compilé y los desensamblé, y creo que he llegado a comprender la razón subyacente: el constructor genera siempre una declaración de retorno implícitamente y esta declaración de retorno es inalcanzable debido a la función noreturn.

noreturn_constructor.cpp

__declspec(noreturn) void foo() { 
    // Do something, then terminate the program 
} 
struct A { 
    A() { 
    foo(); 
    } 
    ~A() { 
    } 
}; 
int main() { 
    A a; 
} 

noreturn_destructor.CPP

__declspec(noreturn) void foo() { 
    // Do something, then terminate the program 
} 
struct A { 
    A() { 
    } 
    ~A() { 
    foo(); 
    } 
}; 
int main() { 
    A a; 
} 

diff -u * .disasm

--- noreturn_constructor.disasm 2012-05-30 11:15:02.000000000 -0400 
+++ noreturn_destructor.disasm 2012-05-30 11:15:08.000000000 -0400 
@@ -2,7 +2,7 @@ 
Copyright (C) Microsoft Corporation. All rights reserved. 


-Dump of file noreturn_constructor.obj 
+Dump of file noreturn_destructor.obj 

File Type: COFF OBJECT 

@@ -35,15 +35,15 @@ 

[email protected]@[email protected] (public: __cdecl A::A(void)): 
    0000000000000000: 48 89 4C 24 08  mov   qword ptr [rsp+8],rcx 
- 0000000000000005: 48 83 EC 28  sub   rsp,28h 
- 0000000000000009: E8 00 00 00 00  call  [email protected]@YAXXZ 
- 000000000000000E: 48 8B 44 24 30  mov   rax,qword ptr [rsp+30h] 
- 0000000000000013: 48 83 C4 28  add   rsp,28h 
- 0000000000000017: C3     ret 
+ 0000000000000005: 48 8B 44 24 08  mov   rax,qword ptr [rsp+8] 
+ 000000000000000A: C3     ret 

[email protected]@[email protected] (public: __cdecl A::~A(void)): 
    0000000000000000: 48 89 4C 24 08  mov   qword ptr [rsp+8],rcx 
- 0000000000000005: C3     ret 
+ 0000000000000005: 48 83 EC 28  sub   rsp,28h 
+ 0000000000000009: E8 00 00 00 00  call  [email protected]@YAXXZ 
+ 000000000000000E: 48 83 C4 28  add   rsp,28h 
+ 0000000000000012: C3     ret 

    Summary 

El código inalcanzable es esta declaración implícita de retorno, que se genera en el constructor, pero no el destructor:

- 000000000000000E: 48 8B 44 24 30  mov   rax,qword ptr [rsp+30h] 
+ 0000000000000005: 48 8B 44 24 08  mov   rax,qword ptr [rsp+8] 
1

El declspec (NoReturn) en foo está produciendo esta advertencia. Le está diciendo al compilador que esta función no regresa. Entonces el compilador emite una advertencia de que su constructor nunca se completará.

+0

Puede que haya sido demasiado vago en mi pregunta original, pero no creo que esto capte la esencia de lo que estoy buscando en una respuesta: ¿por qué un constructor que llama a una función noreturn antes de terminar da como resultado una advertencia de código inalcanzable donde llamar a la misma función noreturn antes de finalizar cualquier otra función ¿** ** no da como resultado una advertencia de código inalcanzable? – mrkj

1

ver http://msdn.microsoft.com/en-us/library/k6ktzx3s(v=vs.80).aspx

"Este atributo __declspec le dice al compilador que una función no vuelve. Como consecuencia, el compilador sabe que el código después de una llamada a un __declspec (NoReturn) función es inalcanzable".

La llave de cierre puede generar código (como destructores de llamada), que no se alcanzará.

+1

¿Podría ser más específico sobre qué código generado es inalcanzable en este caso? La pregunta era ** qué, precisamente, es inalcanzable ** (no "qué significa' __declspec (noreturn) 'significa"). No debería haber código generado para el destructor en este caso; ¿Qué estaría haciendo el destructor? ¿Por qué debería atribuir el código del destructor a la llave de cierre del constructor? – mrkj

+0

Tienes razón, la explicación es muy simple. En cuanto al desmontaje del código, la llave de cierre genera exactamente el mismo código para el constructor que para el destructor. Por lo tanto, a mi entender, la advertencia debe generarse en ambos casos (lo que no esperaría) o en ninguno. Por cierto, un método normal (o virtual) tampoco genera ninguna advertencia. – Stefan

2

No hay destructores para llamar al final de A::A(), por lo que ese no es el problema. Lo que no se puede alcanzar es la construcción real del objeto, que ocurre después de que el constructor haya terminado su ejecución. Como nunca puede terminar, ese código generado por el compilador es inalcanzable.

+1

Creo que es una teoría interesante. Además del cuerpo del constructor especificado por el usuario, ¿qué código se genera para constituir la "construcción real del objeto"? – mrkj

+1

¿Qué código se ejecuta * después de * el constructor definido por el usuario? Me refiero a asignar claramente la memoria, etc. tiene que hacerse * antes de llamar al constructor del usuario, entonces, ¿qué más hay? – Voo

+0

@mrkj: Veo que ha investigado el problema, por lo que no tengo nada que agregar. +1 a su respuesta para los detalles y la confirmación experimental, por cierto. – Gorpik

Cuestiones relacionadas