Развертывание стека с исключениями, гарантируемыми стандартом С++?
Что касается разворачивания стека, в стандарте С++ говорится:
Исключение считается неотображенным после завершения инициализации объекта исключения ([except.throw]) до завершения активации обработчика исключения ([except.handle]). Это включает разматывание стека.
на par 15.5.3 текущего стандарта. Я пытался понять, к чему относится последнее предложение (This includes stack unwindings
):
- Предполагается, что компилятор должен позаботиться о разматывании стека?
- или, если он говорит, что он зависит от компилятора, нужно ли разматывать или нет стек?
Вопрос возникает из следующего фрагмента:
#include <iostream>
#include <exception>
struct S{
S() { std::cout << " S constructor" << std::endl; }
virtual ~S() { std::cout << " S destructor" << std::endl; }
};
void f() {
try{throw(0);}
catch(...){}
}
void g() {
throw(10);
}
int main() {
S s;
f();
//g();
}
Теперь:
- если вы запустили его как-есть (поймать исключение), у вас есть подсказка об отказе стека
- если вы прокомментируете
f();
и раскомментируете g();
(не перехватывая исключение), у вас есть намек на то, что стек не развязан
Итак, оба эксперимента, похоже, в пользу первой пули выше; как clang++, так и g++ соглашаются на результат (но это не дискриминант).
Кроме того, мне очень странно, что стандарт, который очень осторожен в определении времени и продолжительности объекта, оставляет здесь тень.
Может кто-нибудь уточнить? Развертывание стека для исключенных исключений, гарантированных стандартом? Если да, то где? Если нет, то почему?
Ответы
Ответ 1
Развертывание стека для исключенных исключений, гарантированных стандартом?
Непрерывность стека гарантируется только для исключенных исключений ([except.handle]/9):
Если соответствующий обработчик не найден, вызывается функция std::terminate()
; независимо от того, разворачивается ли стек до того, как этот вызов std::terminate()
будет реализован.
Таким образом, это определяется реализацией, в противном случае.
Если нет, почему?
В случае неперехваченного исключения стандарт вызывает std::terminate
. Это означает завершение выполнения программы. Если у вас есть определенный способ регистрации информации о состоянии системы в определенный момент времени, вы можете не хотеть, чтобы это состояние было нарушено путем разворачивания стека.
И если вы этого не делаете... тогда вам все равно.
Если вам действительно нужно, чтобы стек всегда разматывался, вы можете поместить свой код main
(и любые функции потока) в блок try {} catch(...) {throw;}
.