2013-02-08 9 views
7

Badając co exception_ptr robi, standard C++ 11 mówi (18.8.5/7), że:czy rethrow_exception naprawdę może wyrzucić ten sam wyjątek, a nie kopię?

Zastosowanie rethrow_exception na exception_ptr obiektów, które odnoszą się do tego samego obiektu wyjątku nie wprowadzają wyścig danych. [Uwaga: jeśli rethrow_exception ponownie generuje ten sam obiekt wyjątku (zamiast kopii), jednoczesny dostęp do tego obiektu wyjątku rethrown może wprowadzić wyścigu danych ...

nie znajdę przypadek, w którym to dziwne „Uwaga "dotyczy, ponieważ opisany efekt rethrow_exception to" Zgłasza: obiekt wyjątku, do którego odnosi się p ", ale 15.1/3, opisujący ogólny proces generowania wyjątków, który" rzuca wyjątek - inicjuje obiekt tymczasowy, zwany obiektem wyjątku. "

Dziwna notatka oznaczałaby, że rethrow_exception pomija tę inicjalizację kopii. Ale czy to naprawdę możliwe?

+0

Może to po prostu w ruchu? –

+0

Yup, 'std :: rethrow_exception' nie może zostać zaimplementowane za pomocą wyrażenia' throw x; '. (Ale to jest podobne do 'throw;'.) – aschepler

Odpowiedz

3

Tak, wygląda na niedostatek normy. Dla Ponowne generowanie ekspresji jednorazowe tj throw; bez argumentu, 15.1p8 mówi:

rzut ekspresja bez argumentu ponownie generuje aktualnie obsługiwane wyjątku. Wyjątek został reaktywowany przy użyciu istniejącego obiektu wyjątku; nie utworzono nowego obiektu wyjątków. [...]

Czyli:

#include <exception> 
#include <cassert> 
int main() { 
    std::exception *p = nullptr; 
    try { 
     try { 
     throw std::exception(); 
     } catch(std::exception &ex) { 
     p = &ex; 
     throw; 
     } 
    } catch(std::exception &ex) { 
     assert(p == &ex); 
    } 
} 

Jeśli realizacja current_exception kopii aktualnie obsługiwane obiekt wyjątku, nie ma sposobu, aby stwierdzić, czy rethrow_exception kopii, czy nie, ale czy to odnosi się do obiekt wyjątku wtedy możemy sprawdzić:

#include <exception> 
#include <iostream> 
int main() { 
    std::exception_ptr p; 
    try { 
     try { 
     throw std::exception(); 
     } catch(...) { 
     p = std::current_exception(); 
     std::cout << (p == std::current_exception()) << ' '; 
     std::rethrow_exception(p); 
     } 
    } catch(...) { 
     std::cout << (p == std::current_exception()) << '\n'; 
    } 
} 

każdego wdrożenia ja próbowałem na drukach 1 1; 0 0 jest dozwolone, jeśli current_exception kopiuje; 0 1 jest oczywiście niemożliwe, podczas gdy standard w obecnym stanie wydaje się wymagać 1 0.Naprawiono by 18.8.5p10 do wyjaśnienia z językiem podobnym do 15.1p8, pozwalając lub nakazując rethrow_exception, aby nie kopiować obiektu wyjątku wskazanego przez exception_ptr.

Większość Zgłasza: specyfikacji w standardzie tylko wymienić typ (Zgłasza: bad_alloc) lub użyj nieokreślony (Zgłasza: wyjątek typu ...); jedynymi innymi specyfikacjami wyjątków do używania określonego artykułu są te z future::get i shared_future::get, więc każda rozdzielczość powinna prawdopodobnie również rozwiązać te problemy.

+0

Po dalszych dochodzeniach, znalazłem numer LWG, który zaproponował wydanie kopii w tym przypadku (numer 1369). Wygląda na to, że osiedlił się przeciwko niemu, potwierdzając, jak zachowują się implementacje. Uważam jednak, że wyjaśnienie, takie jak proponowane, ma sens. – soulie

2

Tak, jest to możliwe. Mechanizm obsługi wyjątków ma już kopię obiektu, który został pierwotnie wyrzucony, squirreled away w skrytce pamięci prywatnej. Zazwyczaj za pomocą inteligentnego wskaźnika zarządza się licznikiem odwołań dla tej kopii exception_ptr.

Jeśli chodzi o wymagania ogólne, jeśli określony wymóg jest sprzeczny z ogólnym wymogiem, wygasa określone wymaganie.

+0

Ale notatki AFAIK nie są normatywne, więc rethrowing powinien stworzyć wymaganą nową kopię. – soulie

+0

@twicker - możesz mieć rację, że nie jest to poprawnie określone. '' 'exception_ptr' było pierwotnie rozszerzeniem tylko do biblioteki, które wymagało rozszerzenia obsługi wyjątków środowiska wykonawczego, ale bez zmiany języka. Wymaganie nowej kopii oznaczałoby konieczność zapisania wskaźnika do konstruktora kopiowania obiektu; chociaż jest to technicznie wykonalne, znacznie łatwiej jest ponownie użyć starego obiektu. W praktyce nie ma różnicy; możesz napisać konstruktor kopii, który zarejestruje, czy jest wywoływany, ale na bok, nie ma wpływu na żaden kod świata rzeczywistego. –

3

Po powiedzeniu throw x;, obiekt wyjątku ma ten sam typ co x, ale jest kopią.

Kiedy mówisz: std::rethrow_exception(p);, obiektem wyjątku jest rzeczywisty obiekt, do którego odnosi się wskaźnik, i nie są tworzone żadne dalsze kopie.

Zatem jest kilka wątków jednocześnie rethrow wskaźnik wyjątku samo (które są dopuszczone do kopiowania!), To wszystkie one mają odniesienie do samym obiektu.

+0

OK, to ma sens. Mimo to standard jest mylący, gdy opisuje zachowanie wyjątku rethrow_exception jako "Zgłasza: obiekt wyjątku, do którego odnosi się p". – soulie

+0

@twicker: Myślę, że to całkiem jasne. Zawsze istnieje pojęcie "obiektu wyjątku", a 'std :: current_exception' tworzy wskaźnik do tego obiektu. –

Powiązane problemy