2012-11-30 9 views
25

W jaki sposób norma określa czas życia obiektu tymczasowego utworzonego podczas oceny wyrażenia warunku o wartości if?Czas istnienia nienazwanego tymczasowego obiektu zbudowanego w wyrażeniu warunku

Szukałem tej informacji i znalazłem coś podobnego w przykładzie do punktu [10] w 1.9 $, strona 10. (Mam tutaj na myśli Ostateczną wersję roboczą nowej specyfikacji.) Jednak wciąż nie było to jasne (wystarczy) i ponieważ Visual C++ działał inaczej niż moje zrozumienie tego przykładu, zdecydowałem się zapytać.

Proszę podać właściwe odniesienia do specyfikacji.


Jeśli nazwa obiektu to utrzymuje się przez cały if (tak więc zarówno true bloku i false bloku ale jest zniszczone przed if końcach).

Na przykład:

if (MyClass x = f()) { /* ... */ } else { /* ... */ } 
nextInstruction(); 

x mogą być stosowane zarówno w if blokach ale zostaje zniszczony przed nextInstruction zostanie wywołany.

Ale co, jeśli tego nie nazwiesz?

if (f()) { /* ... */ } else { /* ... */ } 
nextInstruction(); 

W moim zrozumieniu odwołania części specyfikacji wartość zwracana przez f() zostanie zniszczony zanim wejdzie jeden z bloków (zarówno dla true lub dla false) wykonanie.

Jednak Visual C++ niszczy ten tymczasowy obiekt tak, jakby został nazwany. (EDIT:..! jak Tino Didriksen wskazał Visual C++ działa dobrze tu i rzeczywiście teraz potwierdzić, że również muszę się pomylić, patrząc na wstępnych wynikach testów)


ten ma znaczenie w niektórych skrajnych przypadkach (nie omawiamy tutaj, jak bardzo są prawdopodobne i czy dobrze jest napisać kod w ten sposób ...).

Na przykład pozwala mieć:

class ScopedLock { 
public: 
    ~ScopedLock() { if (isLocked()) unlock(); } 

    operator bool() const { return isLocked(); } 

    /* ... */ 

}; 

Teraz, jeśli mamy kod jak:

if (ScopedLock lock = resource.lock()) { /* ... */ } 

możemy być pewni, że gdy wykonanie wchodzi blok true posiadamy zasób i że nie będzie być odblokowane, zanim opuścimy ten blok.

Ale co, jeśli ktoś napisał tak:

if (resource.lock()) { /* ... */ } 

Teraz ważne jest w tym momencie destruktora dla tymczasowego ScopedLock zostanie wywołana. Ponieważ określa, czy ten kod jest poprawny, czy nie (w sensie wykorzystania zasobów). (Znów pomińmy dyskusję na temat tego, czy pisanie takiego kodu jest generalnie złe, nie o to pytać ...)

+0

W pełni kompilowany przykład będzie znacznie lepszy, a następnie fragmenty kodu. Twoje pytanie jest trudne do zrozumienia ze względu na złożoność. Zajęło mi trochę czasu, aby zrozumieć, ale jest dobre :) –

Odpowiedz

6

O ile wiem, Visual C++ jest błędny pod tym względem.

Rzeczywiście, tymczasowy jest (prawie zawsze) zniszczone pod koniec pełnego ekspresji, w której jest utworzona:

§12.2/3: [...] Tymczasowe obiekty są niszczone jako ostatni krok w ocenie pełnym ekspresji że (leksykalnie) zawiera punkt gdzie zostały utworzone

Patrząc na definicję oświadczenia selekcji (if i switch), widzimy, że warunek jest wyrażeniem:

§6.4: 
selection-statement: 
    if (condition) statement 
    if (condition) statement else statement 
    switch (condition) statement 

condition: 
    expression 
    type-specifier-seq declarator = assignment-expression 

więc wszelkie uzupełnienia tymczasowe utworzone w warunkach powinny zostać zniszczone przed wykonaniem następujące oświadczenie (chyba związany odniesienie do const).

Zachowanie się przy wprowadzaniu nowej nazwy w stan jest określony w §6.4/3:

Nazwa wprowadzony zgłoszenia w stanie [...] jest w zakresie od swoim punkcie oświadczenie do końca subforements kontrolowane przez warunek.

Tak w przykładzie, x jest w zakresie w obu gałęziach if i zniszczone przed oceny nextInstruction().

2

To co znalazłem w „projektowaniu i Ewolucji C++” na stronie 145:

void h(String s1, String s2) 
{ 
    const char* p; 
    if (p = s1+s2) { 
     // ... 
    } 
} 

Czy zniszczenie obiektu gospodarstwa s1+s2 nastąpić pod koniec stanu lub na koniec całego oświadczenia if? Odpowiedź brzmi, że obiekt posiadający s1+s2 zostanie zniszczony na końcu warunku.

8

Jednak Visual C++ niszczy ten tymczasowy obiekt jakby został nazwany.

Nie, to nie ...

Biorąc pod uwagę kod

#include <iostream> 

struct S { 
    S() { std::cout << "S()" << std::endl; } 
    S(const S&) { std::cout << "S(const S&)" << std::endl; } 
    ~S() { std::cout << "~S()" << std::endl; } 
    operator bool() const { return true; } 
}; 

int main() { 
    std::cout << "main 1" << std::endl; 

    if (S s = S()) { 
     std::cout << "if 1" << std::endl; 
    } 
    else { 
     std::cout << "else 1" << std::endl; 
    } 

    std::cout << "main 2" << std::endl; 

    if (S()) { 
     std::cout << "if 2" << std::endl; 
    } 
    else { 
     std::cout << "else 2" << std::endl; 
    } 

    std::cout << "main 3" << std::endl; 
} 

GNU g ++ 4.5.1 i 4.7.0 g ++ i VC++ 2010 i VC++ 2012 mają dokładnie ten sam wynik:

main 1 
S() 
if 1 
~S() 
main 2 
S() 
~S() 
if 2 
main 3 

gdzie wymieniony tymczasowy jest niszczony po zniszczeniu elementu if/else i nienazwanego tymczasowego na końcu warunku.

+0

Tak! Masz rację. Ponownie przeprowadziłem test ponownie, aby porównać i odkryłem, że ma on takie same wyniki jak twoje. Nie wiem, co jest ze mną nie tak. Musi jakoś dziwnie źle zinterpretować wyjście konsoli ... –

Powiązane problemy