7

Ich arbeite an einer Anwendung, die mit VC9 gebaut wurde und ich habe eine Warnung getroffen, die ich nicht ganz verstehe: Warum gibt es eine "unerreichbarer Code" Warnung auf der schließenden Klammer der Konstrukteur?Nicht erreichbarer Code an der schließenden Klammer des Konstruktors

Der minimale Testfall das Problem zu reproduzieren ist:

__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; 
} 

Dies muss mit/W4 kompiliert werden, um die Warnung auszulösen. Alternativ können Sie mit/we4702 kompilieren, um einen Fehler bei der Erkennung dieser Warnung zu erzwingen.

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 

Kann jemand erklären, was genau nicht erreichbar ist hier? Meine beste Theorie ist, dass es der Destruktor ist, aber ich hätte gerne eine definitive Antwort.

Wenn ich diesen Code warnen-sauber machen möchte, wie kann ich das erreichen? Das Beste, was ich mir vorstellen kann, ist das Konvertieren in einen Kompilierungsfehler.

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

Edit: zur Klärung, wird das Programm mit einer noreturn Funktion endet normalerweise nicht dazu führen, einen unerreichbaren Code auf der Klammer Schließen Warnung, dass Funktionsaufruf umschließt.

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

Ergebnisse in:

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

Weil Sie das Programm in 'foo()' beenden? – juanchopanza

+0

Etwas "privat" machen und es nicht implementieren, ist der übliche Weg, ** C++ 11 ** hat '= delete', um das zu erreichen. –

+1

muss 'foo()' ''__declspec (noreturn)' 'sein? – ixe013

Antwort

3

Gorpik ist auf dem richtigen Weg. Ich habe zwei ähnliche Testfälle erstellt, kompiliert und zerlegt, und ich denke, dass ich den zugrunde liegenden Grund verstanden habe: Der Konstruktor erzeugt immer implizit eine return-Anweisung und diese return-Anweisung ist aufgrund der noreturn-Funktion nicht erreichbar.

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 

Der unerreichbare Code ist diese implizite return-Anweisung, die im Konstruktor erzeugt wird, nicht aber der 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

The declspec (noreturn) auf foo diese Warnung erzeugt. Sie sagen dem Compiler, dass diese Funktion nicht zurückgibt. Daher gibt der Compiler eine Warnung aus, die Ihr Konstruktor niemals ausführen wird.

+0

Ich mag in meiner ursprünglichen Frage zu vage gewesen sein, aber ich denke nicht, dass dies das Wesentliche dessen erfasst, wonach ich in einer Antwort suche: Warum führt ein Konstruktor, der eine noreturn-Funktion aufruft, vor der Beendigung zu einer unerreichbaren Codewarnung das Aufrufen der gleichen Noreturn-Funktion vor dem Beenden einer anderen Funktion führt ** nicht ** zu einer nicht erreichbaren Code-Warnung? – mrkj

1

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

„Das __declspec-Attribut weist den Compiler an, dass eine Funktion nicht zurück. Als Folge der Compiler weiß, dass der Code nach einem Aufruf eines __declspec (noreturn) Funktion nicht erreichbar ist.“

Die schließende geschweifte Klammer kann Code generieren (wie aufrufende Destruktoren), der nicht erreicht wird.

+1

Können Sie genauer angeben, welcher generierte Code in diesem Fall nicht erreichbar ist? Die Frage war: Was genau ist unerreichbar (nicht was "__declspec (noreturn)" bedeutet). In diesem Fall sollte für den Destruktor kein Code generiert werden. Was würde der Destruktor tun? Warum sollte der Code des Destruktors der schließenden Klammer des Konstruktors zugeordnet werden? – mrkj

+0

Sie haben Recht, die Erklärung ist zu simpel. Wenn Sie die Disassemblierung des Codes betrachten, erzeugt die schließende Klammer genau denselben Code für den Konstruktor wie für den Destruktor. Nach meinem Verständnis muss die Warnung in beiden Fällen generiert werden (was ich nicht erwarten würde) oder in keiner. Übrigens erzeugt eine normale (oder virtuelle) Methode auch keine Warnung. – Stefan

2

Es gibt keine Destruktoren am Ende von A::A() aufgerufen werden, so dass das nicht das Problem ist. Was nicht erreicht werden kann, ist die eigentliche Konstruktion des Objekts, die geschieht, nachdem der Konstruktor seine Ausführung beendet hat. Da dieser Code niemals enden kann, ist der vom Compiler generierte Code nicht erreichbar.

+1

Ich denke, das ist eine interessante Theorie. Welcher Code wird neben dem benutzerdefinierten Körper des Konstruktors generiert, um die "tatsächliche Konstruktion des Objekts" zu bilden? – mrkj

+1

Welcher Code wird * nach * dem benutzerdefinierten Konstruktor ausgeführt? Ich meine eindeutig das Zuweisen von Speicher, usw. muss getan werden * bevor * der Benutzer Konstruktor aufgerufen wird, also was gibt es sonst noch? – Voo

+0

@mrkj: Ich sehe, Sie haben das Problem untersucht, also habe ich nichts hinzuzufügen. +1 auf Ihre Antwort für die Details und experimentelle Bestätigung übrigens. – Gorpik