1

Below code exits by giving error

"abort() has been called".

Is it due to destructor throwing exception? I know throwing exception from destructor results in undefined behavior but there are counter arguments also. and moreover the same program works correct in VS 2012.

#include "stdafx.h"
#include<iostream>
using namespace std;
class Txn
{
 public:
     Txn()
     {
        cout<< "in Constructor" << endl;
     };

    ~Txn()
    {
        try
        {
            cout << "in destructor" << endl;
            throw 10;
        }
        catch(int i)
        {
            cout << "in destructor exception" << endl;
            throw;
        }
    }
};

int main()
{
    try
    {
        Txn t;
    }
    catch (int i)
    {
        cout << "Exception" << i << endl;
    }
    return 0;
}

VS2017 release notes does not mention anything around exception handling changes.

So i have below questions:

  1. Is it incorrect to throw exception in destructor from VS2017 onwards? will it always exit the program by calling abort()?
  2. Are there any flags with which we can make it work?

Please suggest.

novice
  • 179
  • 1
  • 5
  • 15
  • 2
    Throwing exception from destructor does not result in undefined behavior. However it is typically not a good practice. – user7860670 Apr 09 '18 at 19:59
  • Did you ignore some warnings from the compiler? Treat warnings as errors. GCC gave this: [Try it online!](https://tio.run/##lZDBTsMwDIbveQprXLbDJHalFRfEG0zinLmmi5QmUeKIoak8OiFtKKFIIOFLrF/O9/82OrfvEVO6UQZ17KhVNrAnOdyLGJTpwciBgpNIELhrBGoZAhwvRlwFuHjSCu8ETJW17a601/LkQhu5bWGjDDxYk8kR2foNZI1Mp5syODZibt4qoyLYv371VV3g8Env6Df4gjl7@wKH26qONaVkPG@VYVC7f3kBXZAcK2v@cv1pOYpp4clukGpauFgti1bjfA7gRtSfc1D4nnR96jnh4yqTWgcrHE8cvYF8jFGk9I7PWvYh7Z/Ie@s/AA "C++ (gcc) – Try It Online") – jxh Apr 09 '18 at 20:07
  • Looks like you should have gotten [C4297](https://learn.microsoft.com/en-us/cpp/error-messages/compiler-warnings/compiler-warning-level-1-c4297). See https://stackoverflow.com/q/31642468/315052 – jxh Apr 09 '18 at 20:14
  • I don't see the purpose of this question, to test the half-legitimate cases of the C++ standard? What kind of problem does it solve? – BJovke Apr 09 '18 at 20:14

3 Answers3

9

The issue here is all destructors by default are noexcept(true). Throwing an exception without changing the will call std::terminate immediately. If we use

class Txn
{
 public:
     Txn()
     {
        cout<< "in Constructor" << endl;
     };

    ~Txn() noexcept(false)
    {
        try
        {
            cout << "in destructor" << endl;
            throw 10;
        }
        catch(int i)
        {
            cout << "in destructor exception" << endl;
            throw;
        }
    }
};

int main()
{
    try
    {
        Txn t;
    }
    catch (int i)
    {
        cout << "Exception" << i << endl;
    }
    return 0;
}

The program runs as expected.


The reason this worked in VS2012 but not VS2017 is before C++11 a destructor could throw without you needing to specify it. With C++11 noexcept specifiers and the change that all destructors are noexcept by default causes it to break in VS2017.

NathanOliver
  • 171,901
  • 28
  • 288
  • 402
  • 1
    Use `/Zc:implicitNoexcept-` to revert back to pre-C++11 behavior for this issue. – jxh Apr 09 '18 at 20:20
4

Destructors are by default expected to not throw exceptions (noexcept). You can tell the compiler that this destructor is not using the default by adding a noexcept(false).

When trying that on this example, we now get a different diagnostic from the compiler - that the destructor will never, ever reach the end. It's not good for a destructor to never fully destroy the object...

To "fix" this I had to make the rethrow conditional with an if.

class Txn
{
 public:
     Txn()
     {
        cout<< "in Constructor" << endl;
     };

    ~Txn() noexcept(false)
    {
        try
        {
            cout << "in destructor" << endl;
            throw 10;
        }
        catch(int i)
        {
            cout << "in destructor exception" << endl;
            if (i == 10) throw;
        }
    }
};
Bo Persson
  • 90,663
  • 31
  • 146
  • 203
-1

Undefined behaviour can be anything, including a call to abort(); Just avoid everything that may induce it.

Throwing from the destructor is not forbidden, but doing that inside a try/catch might result in double exception thrown.

Michael Chourdakis
  • 10,345
  • 3
  • 42
  • 78