7

Pracuję nad aplikacją zbudowaną za pomocą VC9 i trafiłem na ostrzeżenie, którego nie w pełni rozumiem: dlaczego jest ostrzeżenie "nieosiągalny kod" na klamrze zamykającej konstruktor?Nieosiągalny kod na klamrze zamykającej konstruktora

Minimalna testcase odtworzyć problem jest:

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

To musi być skompilowany z/W4, aby wywołać ostrzeżenie. Alternatywnie możesz skompilować z/we4702, aby wymusić błąd w wykryciu tego ostrzeżenia.

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 

Może ktoś wyjaśnić, co dokładnie jest nieosiągalny tutaj? Moja najlepsza teoria mówi, że to destruktor, ale chciałbym uzyskać ostateczną odpowiedź.

Jeśli chcę, aby ten kod był ostrzeżony, jak mogę to osiągnąć? Najlepsze, co mogę wymyślić, to przekonwertować to na błąd podczas kompilacji.

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

Edit: o wyjaśnienie, kończąc program z funkcją noreturn zwykle nie powodują nieosiągalny kod ostrzegawczy na nawiasu zamykającego zamykającego że wywołanie funkcji.

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

Wyniki w:

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

Ponieważ kończysz program w 'foo()'? – juanchopanza

+0

Wykonywanie czegoś "prywatnego", a nie implementowanie go to zwykły sposób, ** C++ 11 ** ma '= delete', aby to osiągnąć. –

+1

musi 'foo()' być '__declspec (noreturn)'? – ixe013

Odpowiedz

3

Gorpik jest na dobrej drodze. Stworzyłem dwa podobne przypadki testowe, skompilowałem je i zdemontowałem i myślę, że zrozumiałem przyczynę: konstruktor zawsze generuje generalnie instrukcję return, a ta instrukcja return jest nieosiągalna z powodu funkcji 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 

Kod nieosiągalny jest to niejawna instrukcja return, która jest wytwarzana w konstruktorze ale nie destruktor:

- 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) na foo produkuje to ostrzeżenie. Mówisz kompilatorowi, że ta funkcja nie wraca. Tak więc kompilator wydaje ostrzeżenie, że twój konstruktor nigdy się nie ukończy.

+0

Mogłem być zbyt niejasne w moim pierwotnym pytaniu, ale nie sądzę, że to przechwytuje istotę tego, czego szukam w odpowiedzi: dlaczego konstruktor wywołujący funkcję noreturn przed zakończeniem skutkuje ostrzeżeniem o nieosiągalnym kodzie, gdzie wywołanie tej samej funkcji noreturn przed zakończeniem jakiejkolwiek innej funkcji powoduje, że ** nie ** skutkuje ostrzeżeniem o nieosiągalnym kodzie? – mrkj

1

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

„To __declspec atrybut informuje kompilator, że funkcja nie zwraca. W konsekwencji, kompilator wie, że kod po wezwaniu do __declspec (noreturn) funkcja jest nieosiągalny.”

Klamra zamykająca może generować kod (np. Wywołujące destruktory), która nie zostanie osiągnięta.

+1

Czy mógłbyś dokładniej określić, w jakim przypadku wygenerowany kod jest niedostępny? Pytanie brzmiało ** co dokładnie jest nieosiągalne ** (nie "co znaczy" __declspec (noreturn) "). W tym przypadku nie powinno być generowanego kodu dla destruktora; co zrobiłby destruktor? Dlaczego miałby on przypisywać kod destruktora do klamry zamykającej konstruktora? – mrkj

+0

Masz rację, wyjaśnienie jest zbyt proste. Patrząc na dezasemblację kodu, nawias zamykający generuje dokładnie taki sam kod dla konstruktora, jak dla destruktora. Zgodnie z moim rozumieniem ostrzeżenie musi zostać wygenerowane w obu przypadkach (czego bym się nie spodziewał) lub w żadnym. BTW, normalna (lub wirtualna) metoda również nie generuje ostrzeżenia. – Stefan

2

Na końcu A::A() nie ma żadnych destruktorów, więc to nie jest problem. Nie można dotrzeć do faktycznej konstrukcji obiektu, która ma miejsce po zakończeniu wykonywania przez konstruktora. Ponieważ nigdy nie może się zakończyć, kod generowany przez kompilator jest nieosiągalny.

+1

Myślę, że to interesująca teoria. Oprócz określonego przez użytkownika ciała konstruktora, jaki kod jest generowany, aby stanowić "faktyczną konstrukcję obiektu"? – mrkj

+1

Który kod jest uruchamiany * po * konstruktorze zdefiniowanym przez użytkownika? Mam na myśli wyraźne przydzielanie pamięci itp. * Przed * wywoływaniem konstruktora użytkownika, więc co jeszcze jest? – Voo

+0

@mrkj: Widzę, że zbadałeś problem, więc nie mam nic do dodania. +1 do swojej odpowiedzi na szczegóły i eksperymentalne potwierdzenie, przy okazji. – Gorpik

Powiązane problemy