2014-04-14 13 views
11

Zainspirowany this question, że próbował następujący kod:warunkowego operatora + uskok + const odniesienia

struct A { 
    virtual void doit() const = 0; 
}; 

struct B : public A { 
    virtual void doit() const; 
}; 

struct C : public A { 
    virtual void doit() const; 
}; 

void 
foo(bool p) 
{ 
    const A &a = (p ? static_cast<const A &>(B()) : static_cast<const A &>(C())); 
    a.doit(); 
} 

Every compiler I have tried przyjmuje tego kodu z -Wall -Werror i generuje się zespół, że ma. Ale po uważnym przeczytaniu specyfikacji C++ 03, sekcja 12.2 ("Tymczasowe") i sekcji 5.12 ("Operator warunkowy"), nie jestem pewien, czy to gwarantuje działanie.

Czy jest to prawidłowy kod, czy też wywołuje niezdefiniowane zachowanie? Czy odpowiedź różni się dla C++ 03 i C++ 11?

Cytaty z odpowiednich specyfikacji byłyby mile widziane.

+0

http://stackoverflow.com/questions/14405837/lifetime-extension-and-the-conditional-operator –

+1

http://www.open-std.org/jtc1/sc22/wg21/docs/cwg_defects .html # 86 –

+0

Wygląda mi doskonale w porządku ... Jaka jest twoja troska? – ildjarn

Odpowiedz

6

To bardzo nieważne.

Rozważmy:

#include <iostream> 
using namespace std; 

struct A { 
    virtual ~A() { cout << "~A" << endl; } 
    virtual void doit() const = 0; 
}; 

struct B : public A 
{ 
    ~B() override { cout << "~B" << endl; } 
    void doit() const override { cout << "A::doit" << endl; } 
}; 

struct C : public A 
{ 
    ~C() override { cout << "~C" << endl; } 
    virtual void doit() const { cout << "C::doit" << endl; } 
}; 

void foo(bool p) 
{ 
    cout << "foo(" << p << ")" << endl; 
    const A &a = (p ? static_cast<const A &>(B()) : static_cast<const A &>(C())); 
    a.doit(); 
} 

auto main(int argc, char* argv[]) -> int 
{ 
    cout << boolalpha; 

    foo(true); 
    cout << endl; 
    foo(false); 
} 

Wyjście w Coliru Viewer, używając g ++ 4.8:

 
foo(true) 

~B 

~A 

pure virtual method called 

terminate called without an active exception 

bash: line 7: 16922 Aborted     (core dumped) ./a.out 

Nadszedł UB więc wszelkie wyjaśnienie może być prawdą, ale można być pewnym, nie patrząc na zespole, to się dzieje:

  • Powstaje tymczasowy.
  • Jest to związane z odniesieniem.
    Jest to odniesienie powiązane z odwołaniem, więc nie wymaga utworzenia nowego tymczasowego lub plasterka.
  • Tymczasowy jest zniszczony.
  • W ramach tego jego dynamiczny typ (wskaźnik vtable) zostaje zmieniony na A, co jest abstrakcyjne.
  • Wywoływany jest czysty wirtualny w A.
+0

Przepraszam za niedoskonały kod, widzę, że nie udało mi się zmienić deklaracji na "przesłonić" wszędzie. I znowu normalny, to jest noc tutaj w Norwegii i potrzebuję odpoczynku, zanim wstaję za kilka godzin. Mimo to, być może odpowiedź powinna zostać rozszerzona o kilka wyjaśnień, że pytanie brzmi * nie * przedłużenie życia. –

+0

Nie wdając się w dyskusję, jak okropny jest dany kod, dlaczego nie jest on tymczasowo związany z odniesieniem do const? Czy nie powinien przedłużyć żywotność do końca zakresu? Myślę, że właśnie tego oczekiwał OP. –

+1

@ BЈовић: jak odnotowano w uwagach Dip w pytaniu, istnieje jeszcze nierozwiązany raport dotyczący usterki dotyczący isse, ** [DR 1376] (http://www.open-std.org/JTC1/SC22/WG21/docs/ cwg_defects.html # 1376) **. Problem polega na tym, że efekt "static_cast" do odniesienia jest zdefiniowany w kategoriach wynalezionego "tymczasowego" obiektu. Ale to tylko słowny problem. Przedłużenie żywotności dotyczy tylko bezpośredniego powiązania tymczasowego. Powiązanie odniesienia z odwołaniem nie wydłuża okresu istnienia, głównie dlatego, że jego właściwe zdefiniowanie (np. Uniemożliwienie odniesienia wywołanego przez wywołanie funkcji) byłoby skomplikowane. –

Powiązane problemy