Różnica między specyfikatorem throw () w C ++ 03 C ++ 11 noexcept

100

Czy jest jakaś różnica między throw()i noexceptinnymi niż bycie sprawdzanymi odpowiednio w czasie wykonywania i kompilacji?

Ten artykuł w Wikipedii o C ++ 11 sugeruje, że specyfikatory rzutów C ++ 03 są przestarzałe.
Dlaczego tak, jest w noexceptstanie pokryć to wszystko w czasie kompilacji?

[Uwaga: sprawdziłem to pytanie i ten artykuł , ale nie mogłem określić solidnego powodu wycofania.]

iammilind
źródło
7
Zgodnie z tym fajnym artykułemnoexcept może również powodować kontrole w czasie wykonywania. Główna różnica między nimi polega na tym, że łamanie noexceptpowoduje, std::terminatea łamanie throwpowoduje std::unexpected. Również nieco inne zachowanie podczas rozwijania stosu w tych przypadkach.
Fiktik
Nie ma nic sprawdzanego „czasu kompilacji” z niektórymi specyfikacjami wyjątków, które są sprawdzane w innych przypadkach. To tylko mit stworzony przez przeciwników specyfikacji wyjątków C ++.
curiousguy

Odpowiedzi:

129

Specyfikatory wyjątków zostały wycofane, ponieważ specyfikatory wyjątków są generalnie okropnym pomysłem . noexceptzostał dodany, ponieważ jest to jedyne rozsądnie użyteczne użycie specyfikatora wyjątku: wiedza, kiedy funkcja nie zgłosi wyjątku. W ten sposób staje się wyborem binarnym: funkcje, które będą rzucać i funkcje, które nie będą rzucać.

noexceptzostał dodany zamiast po prostu usuwać wszystkie specyfikatory rzutów, z wyjątkiem tego, throw()że noexceptjest potężniejszy. noexceptmoże mieć parametr, który w czasie kompilacji zamienia się w wartość logiczną. Jeśli wartość logiczna jest prawdziwa, to noexceptkije. Jeśli boolean ma wartość false, to noexceptnie przykleja się i funkcja może rzucić.

Możesz więc zrobić coś takiego:

struct<typename T>
{
  void CreateOtherClass() { T t{}; }
};

Czy CreateOtherClassrzuca wyjątki? Może, jeśli Tdomyślny konstruktor to potrafi. Jak powiemy? Lubię to:

struct<typename T>
{
  void CreateOtherClass() noexcept(is_nothrow_default_constructible<T>::value) { T t{}; }
};

W ten sposób CreateOtherClass()zgłosi iff domyślny konstruktor danego typu. Rozwiązuje to jeden z głównych problemów ze specyfikatorami wyjątków: ich niezdolność do propagowania stosu wywołań.

Nie możesz tego zrobić throw().

Nicol Bolas
źródło
+1 Przydatna odpowiedź, przynajmniej dla mnie. Wciąż szukam odpowiedzi, która mówi, dlaczego chciałbym użyć noexcept. Nigdy nie korzystałem ze throw()specyfikatora, nigdy i próbuję określić, czy noexceptfaktycznie zapewnia jakiekolwiek korzyści (poza dokumentacją sprawdzoną przez kompilator).
hmjd
Właśnie znalazłem ten stackoverflow.com/questions/10787766/… ...
hmjd
1
@NicolBolas zgadza się. ale jeśli noexcept byłby gwarancją, kompilator mógłby sprawdzić, czy funkcja może zgłosić lub nie w destruktor. W ten sposób można ostrzec programistę, że funkcja nie jest wyjątkiem lub nie.
Alex
2
@NicolBolas wywołuje środowisko wykonawcze std::terminate. co jest o wiele gorzej ! kod może zakraść się do wersji, które mają zaznaczone funkcje, noexcept a w czasie wykonywania (czyli w witrynach klientów) wykrywane są naruszenia. Chodziło mi gwarancje kompilator generuje kod, który nie rzucać wyjątków w pierwszej kolejności.
Alex
2
@NicolBolas: Jeszcze jedna różnica warta odnotowania. Jeśli funkcja jest zaznaczona, throws()to jeśli zostanie zgłoszony wyjątek, stos musi zostać odwinięty do zakresu tej funkcji (więc wszystkie automatyczne zmienne w funkcji są niszczone), w którym terminate()to momencie jest wywoływana (przez unexpected()). Jeśli funkcja jest zaznaczona, noexceptto jeśli zostanie zgłoszony wyjątek, wywoływane jest zakończenie (odwinięcie stosu jest szczegółem zdefiniowanym w implementacji).
Martin York,
33

noexcept nie jest sprawdzana w czasie kompilacji.

Implementacja nie może odrzucać wyrażenia tylko dlatego, że po wykonaniu zgłosi lub może zgłosić wyjątek, na który funkcja zawierająca nie pozwala.

Gdy funkcja, która jest zadeklarowana noexceptlub throw()próbuje zgłosić wyjątek, jedyną różnicą jest to, że wywołania terminatei inne wywołania, unexpecteda ten drugi styl obsługi wyjątków został faktycznie przestarzały.

CB Bailey
źródło
Ale jeśli funkcja wirtualna ma throw()/ noexcept, sprawdzanie czasu kompilacji upewnij się, że nadpisanie również ma.
curiousguy
2

std::unexpected() jest wywoływana przez środowisko wykonawcze C ++, gdy zostanie naruszona dynamiczna specyfikacja wyjątku: wyjątek jest generowany przez funkcję, której specyfikacja wyjątku zabrania wyjątków tego typu.

std::unexpected() można również wywołać bezpośrednio z programu.

W obu przypadkach std::unexpectedwywołuje aktualnie zainstalowany std::unexpected_handler. Domyślną std::unexpected_handlernazywa std::terminate.

ma13
źródło