Why can't Alexandrescu use std::uncaught_exception() to implement SCOPE_FAIL in ScopeGuard11? [duplicate]

断了今生、忘了曾经 提交于 2019-12-04 22:52:13

With a ScopeGuard11 class that has your destructor, the member f_ may be called, even if it is not the current scope (that is supposed to be protected by the guard) that is being exited due to an exception. Use of this guard is unsafe in code that might be used during exception cleanup.

Try this example:

#include <exception>
#include <iostream>
#include <string>

// simplified ScopeGuard11
template <class Fun>
struct ScopeGuard11 {
     Fun f_;
     ScopeGuard11(Fun f) : f_(f) {}
     ~ScopeGuard11(){                      //destructor
        if(std::uncaught_exception()){    //if we are exiting because of an exception
            f_();                         //execute the functor
         }
         //otherwise do nothing
      }
};

void rollback() {
  std::cout << "Rolling back everything\n";
}
void could_throw(bool doit) {
  if (doit) throw std::string("Too bad");
}

void foo() {
   ScopeGuard11<void (*)()> rollback_on_exception(rollback);
   could_throw(false);
   // should never see a rollback here
   // as could throw won't throw with this argument
   // in reality there might sometimes be exceptions
   // but here we care about the case where there is none 
}

struct Bar {
   ~Bar() {
      // to cleanup is to foo
      // and never throw from d'tor
      try { foo(); } catch (...) {}
   }
};

void baz() {
   Bar bar;
   ScopeGuard11<void (*)()> more_rollback_on_exception(rollback);
   could_throw(true);
}

int main() try {
   baz();
} catch (std::string & e) {
   std::cout << "caught: " << e << std::endl;
}

You'd want to see one rollback when leaving baz, but you'll see two - including a spurious one from leaving foo.

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!