Поведение исключения C++14 против C++98

Я написал следующую программу

#include <iostream>
#include <stdexcept>

class Myclass
{
    public:
    ~Myclass() 
    {
        //throw std::runtime_error("second (in destructor)");
        throw 1;
    }
};

void fun()
{
    Myclass obj;
}
int main()
{   
    try
    {
        fun();      
    }
    catch (const std::exception& e)
    {
       std::cout << e.what();
    }
    catch(...)
    {
       std::cout << " ... default Catch" << std::endl; 
    }
    std::cout << "Normal" << std::endl;
    return 0;
}  

Когда я запускаю вышеуказанную программу в режиме C++98 (cpp.sh), она печатает

 ... default Catch
Normal

Когда я запускаю его в режиме C++14, он ничего не печатает. Почему это поведение изменилось?

Я понимаю, что всякий раз, когда возникает какое-либо исключение и любое destructor (в процессе раскручивания стека) выдает любое исключение, оно завершает работу приложения. Но здесь только один раз выбрасывается исключение из блока try, то есть из блока destructor.


person gaurav bharadwaj    schedule 18.04.2017    source источник
comment
Не создавать исключения в деструкторах.   -  person Some programmer dude    schedule 18.04.2017
comment
g++ говорит: main.cpp:12:15: предупреждение: throw всегда будет вызывать terminate() [-Wterminate] main.cpp:12:15: примечание: в деструкторах C++11 по умолчанию используется значение noexcept   -  person aschepler    schedule 18.04.2017
comment
Более актуальный предыдущий вопрос здесь: stackoverflow.com/a/16540757/1858225   -  person Kyle Strand    schedule 18.04.2017


Ответы (2)


Начиная с C++11, деструктор без явно прописанной спецификации исключения имеет ту же спецификацию исключения, что и деструктор, сгенерированный по умолчанию. В вашем случае деструктор, сгенерированный по умолчанию, будет noexcept (большинство деструкторов, сгенерированных по умолчанию), поэтому ваш деструктор также считается noexcept. Выброс из функции noexcept автоматически вызывает std::terminate.

Если вы хотите, чтобы исключение было перехватываемым, объявите деструктор как бросающий:

~Myclass() noexcept(false)
{
    //throw std::runtime_error("second (in destructor)");
    throw 1;
}

Но прежде чем вы это сделаете, подумайте еще раз. Это плохая идея иметь метательные деструкторы.

person Angew is no longer proud of SO    schedule 18.04.2017
comment
noexcept() это... странно. Просто noexcept или, может быть, noexcept(true)? - person T.C.; 19.04.2017

Генерация исключений из деструкторов всегда плохая идея, поскольку, если уже есть исключение в полете (например, деструктор, вызванный во время раскручивания стека), будет вызван std::terminate.

Тем не менее, С++ 11 добавил неявный noexcept(true) к деструкторам (за исключением случаев, перечисленных ниже), и это снова означает std::terminate при выбрасывании из деструктора noexcept(true).

§12.4/3

[class.dtor] [Примечание: объявление деструктора без спецификатора noexcept имеет ту же спецификацию исключения, как если бы оно было объявлено неявно (15.4). — примечание в конце]

и §15.4/14

[except.spec] Спецификация исключения для неявно объявленного деструктора или деструктора без спецификатора noexcept является потенциально генерирующей тогда и только тогда, когда любой из деструкторов для любого из его потенциально сконструированных подобъектов потенциально генерирующий.

Я настоятельно рекомендую реструктурировать ваш код, чтобы не использовать деструкторы.

person Marco A.    schedule 18.04.2017
comment
Есть std::uncaught_exception(s) для обработки таких случаев. :) - person bipll; 19.04.2017