Гарантируется ли разматывание стека с исключениями стандартом c ?

#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();
}
  

Теперь:

  1. если вы запустите его как есть (перехватите исключение), у вас будет подсказка о разматывании стека
  2. если вы комментируете 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(); } }