#c #c 11 #language-lawyer
#c #c 11 #язык-юрист
Вопрос:
Что касается разматывания стека, стандарт c гласит:
Исключение считается неперехваченным после завершения инициализации объекта исключения ([except.throw]) до завершения активации обработчика исключения ([except.handle]). Это включает в себя разматывание стека.
в пункте 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. В опубликованной программе
s
не уничтожается во время разматывания стека. Он уничтожается послеf()
возврата.
Ответ №1:
Гарантируется ли стандартом разматывание стека для неперехваченных исключений?
Разматывание стека гарантированно происходит только для перехваченных исключений ([except.handle]/9):
Если соответствующий обработчик не найден,
std::terminate()
вызывается функция; независимо от того, разматывается ли стек до этого вызоваstd::terminate()
, определяется реализацией.
В противном случае это определяется реализацией.
Если нет, то почему?
В случае неперехваченного исключения вызываются стандартные причины std::terminate
. Это означает конец выполнения программы. Если у вас есть какой-либо специфичный для платформы способ регистрации информации о состоянии системы в то время, вы можете не захотеть, чтобы это состояние нарушалось при разматывании стека.
И если вы этого не делаете … тогда вам все равно в любом случае.
Если вам действительно нужно, чтобы стек всегда разматывался, вы можете поместить свой main
код (и любые потоковые функции) в try {} catch(...) {throw;}
блок.
Комментарии:
1. «Если вам действительно нужно, чтобы стек всегда разматывался, вы можете поместить свой основной код в блок try {} catch(…) {throw;}». — а как насчет потоков? Мое внутреннее чувство говорит о том, что отдельным функциям ввода потока также требуется блок try-catch в этом случае, нет?
2. @JohannGerell: Я думаю, что это UB, если функция потока завершается с исключением.
3. @KerrekSB Нет, это
terminate
.4. Мои программы часто заканчиваются тем, что начинаются с чего-то вроде
int main2(); int main() { try { return main2(); } catch(...) { global_handler(); } }