std::unique_ptr でスコープガード的な何かを作る

お題

当面のスコープガード - Faith and Brave - C++で遊ぼう
のスコープガードを std::unique_ptr を使って実装してみる。
std::unique_ptr を使うことで、 release() によるキャンセルが可能になるし、
メモリ確保の回避や静的解析による最適化で、パフォーマンスの向上が期待できる筈。

できたもの

http://gist.github.com/589772

#include <iostream>
#include <memory>
#include <utility>
#include <functional> // for std::bind

// pointer, deleter のペアから unique_ptr を作る
// 本当は不適切( D に pointer の typedef があった場合など)
template< class T, class D >// = std::default_delete<T> は VC++ 対応のため泣く泣く削除
inline std::unique_ptr<T, D> to_unique( T* p, D d = D() )
{
  return std::unique_ptr<T, D>( p, std::forward<D>(d) );
}

// 実装本体
template<class F>
inline auto scope_exit( F f )
  -> decltype( to_unique( (void*)0, std::bind( std::forward<F>(f) ) ) )
{
  void* const p = reinterpret_cast<void*>(666); // 特に意味のない値。 NULL でなければよい
  return to_unique( p, std::bind( std::forward<F>(f) ) );
}


// テスト
void foo()
{
  std::cout << "start" << std::endl;
  auto const _ = scope_exit( [](){ std::cout << "scope end" << std::endl; } );
  std::cout << "end" << std::endl;
}
void bar()
{
  std::cout << "start" << std::endl;
  auto guard = scope_exit( [](){ std::cout << "scope end" << std::endl; } );
  std::cout << "end" << std::endl;
  guard.release();
}

int main()
{
  foo();
  std::cout << std::endl;
  bar();
}

コンパイルして実行させると

start
end
scope end

start
end

のような出力が得られ、ちゃんとキャンセルできてることが分かる。

解説

scope_exit の実装でラムダ式ではなく std::bind を使っているのは、
ラムダ式を decltype 中で使用することができないため。
また std::bind の使い方が少し妙に思われるかもしれないが、
std::bind は余計な引数を単に無視する効果があるので、これは
「与えられた引数をすべて無視して関数を呼び出すような関数オブジェクトを作る」
という意味になる。

結論

  • std::unique_ptr は使える子
  • でも正直、効率を考えたら専用のクラスを自前で書くほうが良いと思う
  • reinterpret_cast は他に良い方法が思いつかなかったので、ゼロオーバーヘッドで非 NULL なアドレスを得る綺麗な方法が欲しいなーとか
  • gist 便利!(← これが一番大事