17

毫无疑问,很多人都熟悉 Alexandrescus 先生的 ScopeGuard 模板(现在是 Loki 的一部分)以及此处介绍的新版本 ScopeGuard11:http: //channel9.msdn.com/Shows/Going+Deep/C-and-Beyond-2012- Andrei-Alexandrescu-Systematic-Error-Handling-in-C

来源: https ://gist.github.com/KindDragon/4650442

在他在 c++ 和 2012 年以后的演讲中,他提到他找不到正确检测范围是否因异常而退出的方法。因此,当且仅当范围因异常而退出时,他无法实现 SCOPE_FAIL 宏,该宏将执行提供的 lambda(通常用于回滚代码)。这将使dismiss() 成员函数变得不需要,并使代码更具可读性。

由于我绝不像 Alexandrescu 先生那样天才或经验丰富,我希望实现 SCOPE_FAIL 并不像这样容易:

~ScopeGuard11(){                      //destructor
    if(std::uncaught_exception()){    //if we are exiting because of an exception
        f_();                         //execute the functor
    }
    //otherwise do nothing
}

我的问题是为什么不呢?

4

1 回答 1

12

对于ScopeGuard11具有您的析构函数的类,f_可能会调用该成员,即使它不是由于异常而退出的当前范围(应该由守卫保护)。在异常清理期间可能使用的代码中使用此防护是不安全的。

试试这个例子:

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

rollback离开时你想看到一个baz,但你会看到两个——包括离开时的一个虚假的foo

于 2013-02-18T00:29:39.827 回答