right usage of std::uncaught_exception in a destructor

后端 未结 3 2010
佛祖请我去吃肉
佛祖请我去吃肉 2021-01-05 05:06

There are some articles concluding \"never throw an exception from a destructor\", and \"std::uncaught_exception() is not useful\", for example:

  • http://www.got
相关标签:
3条回答
  • 2021-01-05 05:18

    Herb Sutter is talking about the situation when an object of class T is destroyed while there is an uncaught exception in an object of class U. std::uncaught_exception() would return true in the T destructor. The destructor would be unable to find out whether it's called during stack unwinding. If it is, it must not throw, otherwise it's business as usual.

    The class U would have a problem using class T in the destructor. U would find itself dealing with a useless T object that would refuse to do anything risky in its destructor (that could include writing a log file or committing a transaction to a database).

    Herb Sutter suggests never throwing in a destructor, which is a good idea. However, the C++17 offers another option. It introduced std::uncaught_exceptions(), which can be used to find out whether the destructor can throw. Following example shows the problem if complied in C++14 mode. If compiled in C++17 mode, it would work correctly.

    
    #include <exception>
    #include <iostream>
    #include <string>
    
    class T
    {
      public:
    
        ~T() noexcept(false)
        {
    #if __cplusplus >= 201703L
          // C++17 - correct check
          if (std::uncaught_exceptions() == uncaught_exceptions_)
    #else
          // Older C++ - incorrect check
          if (!std::uncaught_exception())
    #endif
          {
            throw (std::string{__PRETTY_FUNCTION__} + " doing real work");
          }
          else
          {
            std::cerr << __PRETTY_FUNCTION__ << " cowardly quitting\n";
          }
        }
    
      private:
    
    #if __cplusplus >= 201703L
        const int uncaught_exceptions_ {std::uncaught_exceptions()};
    #endif
    };
    
    class U
    {
      public:
    
        ~U()
        {
          try
          {
            T t;
          }
          catch (const std::string &e)
          {
            std::cerr << __PRETTY_FUNCTION__ << " caught: " << e << '\n';
          }
        }
    };
    
    int main()
    {
      try
      {
        U u;
        throw (std::string{__PRETTY_FUNCTION__} + " threw an exception");
      }
      catch (const std::string &e)
      {
        std::cerr << __PRETTY_FUNCTION__ << " caught: " << e << '\n';
      }
      return 0;
    }
    
    0 讨论(0)
  • 2021-01-05 05:36

    There's nothing technically wrong with your code. It's perfectly safe in that you will never accidentally terminate because you threw an exception when it was not safe to. The issue is that it also is not useful, in that it will occasionally also not throw an exception when it is safe to. Your destructor's documentation basically has to say "this might or might not throw an exception."

    If it occasionally won't throw an exception, you might as well never throw an exception. That way, you're at least consistent.

    0 讨论(0)
  • 2021-01-05 05:39

    Herb Sutter is referring to a different issue. He's talking about:

    try
    {
    }
    catch (...)
    {
        try
        {
            // here, std::uncaught_exception() will return true
            // but it is still safe to throw an exception because
            // we have opened a new try block
        }
        catch (...)
        {
        }
    }
    

    So the problem is that if std::uncaught_exception() returns true you don't know for sure whether you can safely throw an exception or not. You end up having to avoid throwing an exception when std::uncaught_exception() returns true just to be safe.

    0 讨论(0)
提交回复
热议问题