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

2012 年以降の c++ での講演で、彼は、例外が原因でスコープが終了したかどうかを正しく検出する方法を見つけることができなかったと述べました。したがって、彼は、例外のためにスコープが終了した場合にのみ、提供されたラムダ (通常はロールバック コードに使用される) を実行する SCOPE_FAIL マクロを実装できませんでした。これにより、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あなたは去るときに1つを見たいと思うbazでしょう、しかしあなたは2つを見るでしょう-去ることからの偽の1つを含みますfoo

于 2013-02-18T00:29:39.827 に答える