2016-06-13 28 views
9

Spójrz na ten kod, który powoduje zakończenie programu bez przechwytywania wyjątku.Dlaczego wyjątki od unique_ptr destructor kończą program?

#include <iostream> 
#include <string> 
#include <memory> 
#include <stdexcept> 
using namespace std; 

struct test { 
    ~test() noexcept(false) { 
    throw runtime_error("-my-cool-exception-"); 
    } 
}; 

int main() 
{ 
    try { 
    auto ptr = unique_ptr<test>(new test()); 
    //test t; // this is ok, without unique_ptr<test> it works fine. 
    } 
    catch(exception& e) { 
    cout << "this is not called, the program is aborted"; 
    cout << e.what() << endl; 
    } 
    return 0; 
} 

To pytanie różni się od pytania dotyczącego przepełnienia stosu: throwing exceptions out of destructor.

Różnica polega na tym, że wyjątek nie zostanie przechwycony tylko wtedy, gdy używam unique_ptr<test>.

można zobaczyć na żywo kodu, edytować i skompilować tutaj http://cpp.sh/9sk5m

+6

Ponieważ 20.10.1.2.2 wymaga, aby usunięcie nie powodowało wyjątków. Naruszasz warunki wstępne biblioteki. –

+0

Mam bibliotekę, która całkowicie od tego zależy, nie mogę jej złamać. Czy istnieje obejście tego problemu? dzięki. – amin

+9

Obejście problemu: napraw bibliotekę, zrób to teraz, zanim [dalej Cię ugryzie] (http://stackoverflow.com/a/130123/3426025). – BeyelerStudios

Odpowiedz

13

co wymagane przez normy. Zgodnie unique.ptr.single.dtor, 20.10.1.2.2.1:

wymaga: get_deleter ekspresji() (get()) powinny być dobrze wykształcone, mają dobrze zdefiniowane zachowanie, a nie powinny rzucać wyjątki. [Uwaga: użycie default_delete wymaga, aby T było pełnym typem. - koniec uwaga]

(Kopalnia nacisk)

Tak naprawdę, to nie ma znaczenia, czy się ma noexcept(false) destruktor czy nie. Zabronione jest rzucanie w tym przypadku - ostatnie słowo.

Jest to ogólnie rzecz biorąc przypadek std:: - z wyjątkiem sytuacji, gdy określono inaczej. Zgodnie res.on.functions, 17.6.4.8.2.4:

W szczególności, efekty nie są zdefiniowane w następujących przypadkach: [...] jeżeli żadna funkcja wymiany lub funkcję obsługi lub działanie destructor wychodzi poprzez wyjątkiem, chyba że pozwala w stosowne Wymagane zachowanie: akapit.

(Kopalnia nacisk)

Uwaga: W ogóle, może nie rzuca destruktorów z noexcept(false). Jest to jednak bardzo niebezpieczne, ponieważ zostanie wywołane std::terminate, jeśli stos był rozwijany ze względu na zgłoszony wyjątek. Zgodnie except.ctor, 15.2.1:

miarę upływu sterujących z punktu, w którym jest wyjątek do obsługi destruktory są wywoływane przez proces, określone w niniejszym rozdziale, zwany stos odwijania. Jeśli destruktor wywoływany bezpośrednio przez wyjmowanie stosu wychodzi z wyjątkiem, wywoływane jest std :: termin ([except.terminate]). [Uwaga: W konsekwencji destruktory powinny generalnie wychwytywać wyjątki i nie pozwalać im na propagowanie z destruktora. - koniec uwaga]

(Kopalnia nacisk)

See it live on Coliru.

+1

Należy zauważyć, że nawet jeśli podasz * twój * destruktor 'noexcept (false)', to nie zmienia faktu, że destruktor 'unique_ptr' pozostaje' noexcept'. Więc wyjątek zostanie tam połknięty. –

+0

"połknięcie" oznacza, że ​​znika. Tak nie jest. – Ven

+0

OK, "eksplodować" jest prawdopodobnie bardziej precyzyjnym określeniem niż "połknąć";) Ale tak czy inaczej, nie zobaczysz, że ten wyjątek osiąga twoje "catch", bez względu na to, jak bardzo używasz 'noexcept (false)' . –