C++/Boost 小ネタ
コードだけ貼って全く説明しないのもアレかと思うので、汎用性のある手法をいくつか抜き出してみました。
boost::thread_specific_ptr
Boost 版 TLS (Thread Local Storage)。thread_local がサポートされていない、でも __declspec(thread) や __thread は使えない理由がある、なんてときに。
// thread_local A a; A &a() { static boost::thread_specific_ptr<A> p; if (!p.get()) p.reset(new A()); return *p; }
boost::move
Move semantics がサポートされていない環境ではそれをエミュレートし、サポートされていればそのまま使ってくれるという優れもののライブラリ。使い方の詳細はリファレンス参照。
boost::interprocess::unique_ptr
std::unique_ptr の C++03 でも使える版。デリータは常に明示する必要があります。単に delete するだけなら boost::checked_deleter をどうぞ。
boost::interprocess::unique_ptr<T, boost::checked_deleter<T> > p(new T()); // だいたい std::unique_ptr<T> p(new T()); と同じ
template <class T> ...(T &&t) の罠
こんな型 A で、
struct A { std::function<void ()> f_; // 1. コピーコンストラクタ A(A const &) = default; // 2. ムーブコンストラクタ A(A &&) = default; // 3. 引数で f_ を初期化するコンストラクタ template <class F> A(F &&f) : f_(f) {} };
A のコンストラクタに A の lvalue、A const の rvalue を渡したとき、それぞれどのコンストラクタが呼ばれるかという問題。
答えはどちらも 3。template <class F> ...(F &&f) で受けると型が完全にマッチしてしまうため、思いもかけないオーバーロード解決が起こるという例です。
一つの回避方法としては、SFINAE で A の属をはじくという手がありますが、
struct A { std::function<void ()> f_; // 中略 // 3. 引数で f_ を初期化するコンストラクタ。A は受け付けない。 template < class F, typename std::enable_if< !std::is_same<typename std::decay<F>::type, A>::value >::type * = nullptr> A(F &&f) : f_(f) {} };
そもそも危険な F && は避けるという考え方もあります。
struct A { std::function<void ()> f_; // 中略 // 3. 引数で f_ を初期化するコンストラクタ。 template <class F> A(F f) : f_(std::move(f)) {} };
bind
boost::bind, boost::lambda::bind, boost::phoenix::bind, std::bind と選り取り見取り。Move semantics を活用したければ std::bind ですね。
boost::bind は _1, _2, ... がグローバルにあるように見えるので好きではないですが、__stdcall などに対応しているという利点があったりもします。