Generic Lambdas で has_xxx を実装する
has_xxx とは型 x にネストされた名前 name があるかどうか調べるメタ関数の総称です。名前空間またはクラスのスコープでクラステンプレート has_name を定義し、その上で has_name<x>::value などとして使います。
本の虫: C++11におけるモダンなhas_xxxの実装
しかし、一発で has(x, name) と書けると素敵です。C++14 で入るであろう Generic Lambdas (N3559) を使ってそれを書いてみました。
※ Concept Lite (N3580) の requires expression が来れば、素直にそれを使うべきです。
// 下準備... namespace has_xxx_detail { template <class F, class G> struct overload_result : F, G { overload_result(F f, G g) : F(f), G(g) {} using F::operator(); using G::operator(); }; template <class F, class G> overload_result<F, G> overload(F f, G g) { return {f, g}; } template <class T> struct identity { using type = T; }; struct bool_constant { constexpr operator std::true_type() { return {}; } constexpr operator std::false_type() { return {}; } }; } // namespace has_xxx_detail // x にネストされた型 name があるか調べる #define HAS_TYPE(x, name) \ ( \ false ? \ ::has_xxx_detail::overload( \ [](auto t, typename decltype(t)::type::name *) -> std::true_type { return {}; }, \ [](...) -> std::false_type { return {}; } \ )(::has_xxx_detail::identity<x>(), nullptr) : \ ::has_xxx_detail::bool_constant() \ ).value \ /**/ // x にネストされた非型 name があるか調べる #define HAS_VALUE(x, name) \ ( \ false ? \ ::has_xxx_detail::overload( \ [](auto t, decltype(&decltype(t)::type::name) *) -> std::true_type { return {}; }, \ [](...) -> std::false_type { return {}; } \ )(::has_xxx_detail::identity<x>(), nullptr) : \ ::has_xxx_detail::bool_constant() \ ).value \ /**/ struct yes { using type = int; static constexpr int value = 0; }; struct yes_for_value { void value() {} }; static_assert(HAS_TYPE(yes, type), ""); static_assert(HAS_VALUE(yes, value), ""); static_assert(!HAS_TYPE(yes_for_value, value), ""); static_assert(HAS_VALUE(yes_for_value, value), ""); static_assert(!HAS_VALUE(int, value), "");
こちらの Clang バイナリで動作を確認しました。ラムダ式を使っているため、unevaluated operand (decltype の中など) では使えないことに注意です。
なぜ Generic Lambdas でこれを実装できるというと、Generic Lambdas によって擬似的に式の中にテンプレートを書くことができるようになるからです。制約が強いので Generic Lambdas をそのままメタプログラミングに使うのは難しいですが、うまく使えば面白いことができそうです。
Boost.Asioでio_service::run()を包含する設計
Boost.Asio io_serviceを包含する設計 - Faith and Brave - C++で遊ぼう
上の話に加えて、io_service::run()を誰が呼ぶかという話についてのメモ。
1つは上で紹介されているように、io_serviceを公開し、ユーザーにrun()させるパターン。
class client { public: client() : m_io_service(new boost::asio::io_service()) {} explicit client(boost::asio::io_service &io_service) : m_io_service(&io_service, [](boost::asio::io_service *) {}) {} boost::asio::io_service &get_io_service() const noexcept { return *m_io_service; } void start() { // ... } private: std::shared_ptr<boost::asio::io_service> m_io_service; }; void f() { client client_; client_.start(); client_.get_io_service().run(); }
もう1つはcpp-netlibがそうしているように、自分がio_serviceを作った場合は自分でrun()するパターン。
class client { public: client() : m_io_service(new boost::asio::io_service()), // run()がすぐ終了しないよう、workを注入する m_work(new boost::asio::io_service(*m_io_service)), // スレッドを立ててrun()を呼び出す m_thread([=] { m_io_service->run(); }) {} explicit client(boost::asio::io_service &io_service) : m_io_service(&io_service, [](boost::asio::io_service *) {}) {} ~client() { if (m_work) { cancel(); // workを削除して、run()が終了できるようにする m_work.reset(); // run()の終了を待つ m_thread.join(); } } void start() { // ... } private: std::shared_ptr<boost::asio::io_service> m_io_service; std::unique_ptr<boost::asio::io_service::work> m_work; std::thread m_thread; };
どちらを使うかはお好みです。
なお個人的には常にio_serviceを渡してもらえばいいと思っているもよう。
Boost.Asioで非同期操作中に*thisの寿命が尽きることを防ぐ
Boost.Asioを使って非同期操作を書いていると、メンバ関数から起動した非同期操作の最中に*thisの寿命が尽きて困ることがあります。
例えば次のHTTPクライアント:
class http_client { public: // ... void start() { // ... // 名前解決を開始する m_resolver.async_resolve( query, std::bind(&http_client::handle_resolve, this, _1, _2)); } private: // ... // リクエスト書き込みが終了したときに呼ばれる void handle_write( error_code const &error, std::size_t bytes_transferred) { // ... // レスポンスを読み込む asio::async_read( m_socket, m_streambuf, std::bind(&http_client::handle_read, this, _1, _2)); } // レスポンス読み込みが終了したときに呼ばれる void handle_read( error_code const &error, std::size_t bytes_transferred) { // ... } asio::streambuf m_streambuf; };
このコードに潜在するリスクの1つは、asio::async_read(m_socket, ...)によって開始される非同期操作中に、*thisの寿命が尽きてしまう可能性があることです。その場合、バッファ(m_streambuf)が読み込み中に破壊されたり、ハンドラ内で既に破壊されたメンバ変数にアクセスしたりして、意図しない動作が引き起こされます(dangling reference)。
この問題は、http_clientクラスのデストラクタで非同期操作をキャンセルするだけでは解決しません。socket::cancel()を呼び出しても、既にio_serviceにpost()されたハンドラは実行されますし、cancel()はハンドラの終了を待ってくれません(I/O objectのcancel()はそういう動作をします)。
ユーザーとの間で、非同期操作の終了まで*thisを破棄しないという同意が何らかの形で成立していればいいですが、できればクラスの中だけでこの問題を解決したいものです。
*thisをshared_ptrで管理する
この問題を解決するよく知られた方法は、*thisをshared_ptrで管理するようにし、ハンドラに*thisを指すshared_ptrを"埋め込む"ことで、非同期操作が終了するまで*thisを延命する方法です。具体的には、enable_shared_from_thisを用います。
class http_client : public std::enable_shared_from_this<http_client> { public: // ... private: // ... void handle_write( error_code const &error, std::size_t bytes_transferred) { // ... asio::async_read( m_socket, m_streambuf, // ハンドラに*thisを指すshared_ptrを埋め込む std::bind(&http_client::handle_read, shared_from_this(), _1, _2)); } void handle_read( error_code const &error, std::size_t bytes_transferred) { // ... } asio::streambuf m_streambuf; };
この方法の欠点は、クラスのインスタンスを常にshared_ptrで管理することをユーザーに強制する点です。
非同期操作で使うメンバ変数が延命できればそれでいい場合は、そのメンバ変数のみshared_ptrで管理するという手もありますが、*thisそのものは延命できません。
class http_client { public: // ... private: // ... void handle_write( error_code const &error, std::size_t bytes_transferred, std::shared_ptr<asio::ip::tcp::socket> const &socket) { // ... // バッファをメンバ変数でなく、shared_ptr型のローカル変数として定義する auto const streambuf = std::make_shared<asio::streambuf>(); asio::async_read( *socket, *streambuf, // ハンドラにバッファを指すshared_ptrを埋め込む std::bind(&http_client::handle_read, this, _1, _2, socket, streambuf)); } void handle_read( error_code const &error, std::size_t bytes_transferred, std::shared_ptr<asio::ip::tcp::socket> const &socket, std::shared_ptr<asio::streambuf> const &streambuf) { // ... } // asio::streambuf m_streambuf; };
この方法は、非同期操作で使うメンバ変数を局在化するために用いることもできます(Boost.Asio 送受信バッファをメンバ変数に持たないためのイディオム - Faith and Brave - C++で遊ぼう)。
デストラクタでwaitする
非同期操作が終了するまで待つwait()を用意しておけば、デストラクタでwait()を呼ぶことで、非同期操作中の*this破壊を回避することができます。実装としてはpromise/futureを使うのが良さそうです。
class http_client { public: // ... ~http_client() { cancel(); wait(); } void start() { // ... // promiseを作成する auto const promise = std::make_shared<std::promise<void>>(); // futureをメンバ変数に保存する m_futures.push_back(promise->get_future()); m_resolver.async_resolve( query, // promiseをハンドラに埋め込む std::bind(&http_client::handle_resolve, this, _1, _2, promise)); } // 全ての非同期操作が終了するまでブロックする関数 void wait() { // futureのshared stateがreadyになるのを待つ for (auto &future : m_futures) future.wait(); } private: // ... void handle_write( error_code const &error, std::size_t bytes_transferred, std::shared_ptr<std::promise<void>> const &promise) { // ... asio::async_read( m_socket, m_streambuf, // promiseを次のハンドラに渡す std::bind(&handle_read, this, _1, _2, promise)); } void handle_read( error_code const &error, std::size_t bytes_transferred, std::shared_ptr<std::promise<void>> const &promise) { // ... } // 非同期操作が終了した時点で、 // ハンドラに埋め込まれていたpromiseが破壊され、 // shared stateがreadyになる std::vector<std::future<void>> m_futures; asio::streambuf m_streambuf; };
一連のハンドラ呼び出しが終了した時点で、ハンドラに埋め込まれていたpromiseは破壊され、メンバ変数のfutureが参照しているshared stateがreadyになります(30.6.5/7、30.6.4/7)。wait()はfuture::wait()を呼び出してこれを待ちます。
C++/Boostでオブジェクトへのアクセスを同期する方法メモ
C++で、複数のスレッドから1つのオブジェクトにアクセスするときに同期をとる方法いろいろ。
1. atomicを使う
使える型は限られるが、お手軽。
std::atomic<int> x(0); boost::thread_group threads; for (int i = 0; i < 4; ++i) { threads.create_thread([&] { for (int i = 0; i < 1000000; ++i) ++x; }); } threads.join_all(); std::cout << x << std::endl; // 4000000
これはatomic<T>を使う例。
アトミックな操作を順に実行するだけのコードはアトミックでないことに注意。
2. クラスの中でロックする
class integer { public: int get_value() const { std::lock_guard<std::mutex> lock(m_mutex); return m_value; } void set_value(int value) { std::lock_guard<std::mutex> lock(m_mutex); m_value = value; } private: mutable std::mutex m_mutex; int m_value; };
メンバ関数内でロックをかける。内部ロックと呼ぶことにする。Javaでいうところのsynchronizedメソッドか。
こちらも、単に"synchronizedメンバ関数"の呼び出しを連ねただけのコードはアトミックでないことに注意する必要がある。
2.1. 内部ロック:read-lock/write-lockを使う
class integer { public: int get_value() const { boost::shared_lock<boost::shared_mutex> read_lock(m_mutex); return m_value; } void set_value(int value) { boost::upgrade_lock<boost::shared_mutex> upgrade_lock(m_mutex); if (m_value != value) { boost::upgrade_to_unique_lock<boost::shared_mutex> write_lock(upgrade_lock); m_value = value; } } private: mutable boost::shared_mutex m_mutex; int m_value; };
場合によっては、read-lock/write-lockを使うと効率が上がる…かもしれない(この例はてきとー)。
難しい。
3. クラスの外からロックする
class A { public: using mutex_type = std::mutex; mutex_type &get_mutex() const { return m_mutex; } void f() { // ... } void g() { // ... } private: mutable mutex_type m_mutex; }; void fg(A &a) { // 外からロックする。 std::lock_guard<A::mutex_type> lock(a.get_mutex()); // 以降の操作全体がアトミックに行われる。 a.f(); a.g(); }
これを外部ロックと呼ぶことにする。
しかし、外部ロックの作成を忘れた場合は全く同期されないアクセスが行われてしまう。
それはよろしくないので、内部ロックを併用する。
3.1. 外部ロックと内部ロックを組み合わせる
class A { public: using mutex_type = std::recursive_mutex; mutex_type &get_mutex() const { return m_mutex; } void f() { std::lock_guard<mutex_type> lock(m_mutex); // ... } void g() { std::lock_guard<mutex_type> lock(m_mutex); // ... } private: mutable mutex_type m_mutex; }; void f(A &a) { a.f(); } void fg(A &a) { std::lock_guard<A::mutex_type> lock(a.get_mutex()); a.f(); a.g(); }
基本は内部ロックを使い、複数の操作を行う際は外部ロックを使ってブロック全体をアトミックにする。
同一スレッドから2重にロックする場合があるため、recursive_mutexを使う必要がある。
3.1.1. 外部ロックと内部ロックを組み合わせる:クラスをLockableにする
boost::(basic|timed|shared)_lockable_adapterを使うと楽。
class A : public boost::basic_lockable_adapter<boost::recursive_mutex> { public: void f() { boost::lock_guard<A> lock(*this); // ... } void g() { boost::lock_guard<A> lock(*this); // ... } }; void fg(A &a) { boost::lock_guard<A> lock(a); a.f(); a.g(); }
単に見た目の違いだが、割と直感的で良いかもしれない。
いつも思うが、adapter/adaptorはBoost内で統一してほしい。
3.2. boost::externally_locked
class A { public: void f() { // ... } void g() { // ... } }; class B { public: void a_fg() { boost::strict_lock<boost::mutex> lock(m_mutex); auto &a = m_a.get(lock); // 内部のAオブジェクトを取得する a.f(); a.g(); } private: boost::mutex m_mutex; // 内部にAオブジェクトを保有している boost::externally_locked<A, boost::mutex> m_a { m_mutex }; };
ロックのlvalueをexternally_locked::getに渡さないと内部のオブジェクトがとれないため、必然的にローカル変数lockを書くことになり、同期ブロックが導入される。
インターフェースが自然に実装を規定している。良い。
3.3. ロックを内包するスマートポインタ
C++11 std::unique_lock の用例「ロック付きスマートポインタ」 - flatlineの日記とか。
どうせならポインタを介さないとアクセスできないようにする。
下のコードで、sync<T>はTのオブジェクトを保有し、sync<T>::lock()でロックされたTへのポインタを返す。
template <class T> struct sync { public: sync() = default; explicit sync(T const &obj) : m_obj(obj) {} explicit sync(T &&obj) : m_obj(std::move(obj)) {} template <class U> class basic_pointer { public: using element_type = U; basic_pointer(std::mutex &mutex, U &obj) : m_lock(mutex), m_ptr(std::addressof(obj)) {} U &operator*() const noexcept { return *m_ptr; } U *operator->() const noexcept { return m_ptr; } U *get() const noexcept { return m_ptr; } private: std::unique_lock<std::mutex> m_lock; U *m_ptr; }; using pointer = basic_pointer<T>; using const_pointer = basic_pointer<T const>; pointer lock() { return { m_mutex, m_obj }; } const_pointer lock() const { return { m_mutex, m_obj }; } const_pointer clock() const { return lock(); } private: std::mutex m_mutex; T m_obj; }; void fg(sync<A> &a) { // pが存在している間、Aはロックされる auto const p = a.lock(); p->f(); p->g(); // これを禁止して、ローカル変数pの作成をやんわりと強制することもできる。 // a.lock()->f(); }
追記:Boostにある(入る予定)らしい。
URL 3-3に関してはBoost.Threadでboost::synchronized_valueってのが実装されて正にこれなはず trunkには入ってて1.54で多分来る
その道の人が書くとこうなるらしい。実用例 - くまメモ
4. おまけ
Boost.Threadはロックのファクトリ関数を提供する。地味に便利。
// Mutexの型を書くのがめんどい // boost::unique_lock<boost::mutex> lock(mutex); // Good! auto const lock = boost::make_unique_lock(mutex);
make_lock_guardもあるが、lock_guardはムーブできないので参照で受けるとかする。
auto const &lock = boost::make_lock_guard(mutex);
タグ付きバリアント型(代数的データ型)
タグ付きタプル型 - yohhoyの日記
タグ付きバリアント型があってもいいじゃない。
Boost.Variant では要素の型で要素にアクセスしますが、代わりにタグ経由でアクセスすることで、
- 要素の意味が明確になる。
- 複数の同じ型の要素に別の意味を与えることができる。
という利点が得られます。
同等のことは言語機能の union でも可能ですが、union を直接扱うのは面倒です。
ここでは Boost.Variant をラップしてタグ付きバリアント型としてみました。
使い方を示します。
準備
#include "named_variant.hpp" namespace nv = named_variants;
基本
// タグ宣言 struct I {}; struct D {}; // タグ付きバリアント // data Number = I Int | D Double using Number = nv::named_variant<I, int, D, double>; void example1() { // 初期化 Number n = nv::init<I>(42); // 格納されている型は nv::type() 関数で取得する if (nv::type(n) == typeid(I)) { // 値の取り出しは nv::get<タグ> で行う std::cout << "I " << nv::get<I>(n) << std::endl; } n = nv::init<D>(3.14); // パターンマッチ nv::match<void> ( n , nv::case_<I>([](int i) { std::cout << "I " << i << std::endl; }) , nv::case_<D>([](double d) { std::cout << "D " << d << std::endl; }) ); }
再帰データ型
struct Leaf {}; struct Branch {}; // 二分木 // data Tree a = Leaf a | Branch (Tree a) (Tree a) template <class T> using Tree = typename nv::make_recursive_named_variant < Leaf, T , Branch, std::pair<nv::recursive_named_variant_, nv::recursive_named_variant_> >::type; // 木の中に要素があるか調べる(深さ優先) template <class T, class TreeT> bool elem(T const &x, TreeT const &tree) { return nv::match<bool> ( tree , nv::case_<Leaf>([&](T const &y) { return x == y; }) , nv::case_<Branch>([&](std::pair<TreeT, TreeT> const &l_r) { return elem(x, l_r.first) || elem(x, l_r.second); }) ); } void example2() { Tree<int> const t = nv::init<Branch>(std::make_pair ( nv::init<Branch>(std::make_pair ( nv::init<Leaf>(1) , nv::init<Leaf>(2) )) , nv::init<Leaf>(3) )); if (elem(2, tree)) std::cout << "'tree' contains 2." << std::endl; }
Either
struct Left {}; struct Right {}; template <class T, class U> using Either = nv::named_variant<Left, T, Right, U>; void example3() { // 同じ型でも OK Either<std::string, std::string> const e = nv::init<Right>("Hello, world!"); nv::match<void> ( e , nv::case_<Left>([](std::string const &) {}) , nv::case_<Right>([](std::string const &s) { std::cout << s << std::endl; }) ); }
実装は https://gist.github.com/4054177 から。
named_variant<Name, Type, ...> は単に variant<fusion::pair<Name, Type>, ...> のエイリアスになっています。
HaskellのIntegerをC++で使う
C++の標準には多倍長整数ライブラリがありません。これは大変なことだと思います。
まあBoost.Multiprecision来るじゃないというのはさておき、
標準の多倍長整数がないなら他言語の標準から持ってくればいいということで、HaskellのIntegerをラップしてC++の多倍長整数クラスにしてみました。
まずIntegerを扱う関数をFFIでエクスポートします。Integerを引数・戻り値にする関数はエクスポートできないので、代わりにStablePtr Integerを使います。これはIntegerオブジェクトを指すポインタで、GCに回収・再配置されない保証があります。CやC++からはただのvoid *に見えます。
とりあえずfromIntegral、(*)、showをエクスポートしました(今回は乗法と出力がしたいので)。StablePtr IntegerとCStringの解放関数も一緒にエクスポートしておきます。
HsInteger.hs
module HsInteger where import Foreign import Foreign.C.String import Foreign.C.Types import Foreign.Marshal.Alloc import Control.Monad type CInteger = StablePtr Integer foreign export ccall hs_integer_from_int :: CInt -> IO CInteger hs_integer_from_int (CInt n) = newStablePtr $! fromIntegral n foreign export ccall "hs_integer_free" freeStablePtr :: CInteger -> IO () foreign export ccall hs_integer_multiply :: CInteger -> CInteger -> IO CInteger hs_integer_multiply m n = liftM2 (*) (deRefStablePtr m) (deRefStablePtr n) >>= (newStablePtr $!) foreign export ccall hs_integer_to_string :: CInteger -> IO CString hs_integer_to_string = deRefStablePtr >=> newCString . show foreign export ccall "hs_integer_free_string" free :: CString -> IO ()
コンパイルしてHsInteger_stub.hとHsInteger_stub.oを作ります。
$ ghc -c HsInteger.hs
C++側ではこれらの関数をラップしてhs_integerクラスに収めます。
hs_integer.hpp
#ifndef HS_INTEGER_HPP #define HS_INTEGER_HPP #include "HsInteger_stub.h" #include <memory> #include <ostream> #include <boost/operators.hpp> class hs_integer : public boost::multipliable<hs_integer> { public: hs_integer(int n = 0) : ptr(hs_integer_from_int(n), &hs_integer_free) {} hs_integer &operator*=(hs_integer const &rhs) { ptr.reset(hs_integer_multiply(ptr.get(), rhs.ptr.get()), &hs_integer_free); return *this; } template <class Char, class CharTraits> friend std::basic_ostream<Char, CharTraits> & operator<<(std::basic_ostream<Char, CharTraits> &os, hs_integer const &n) { std::unique_ptr<void, void (*)(void *)> const str(hs_integer_to_string(n.ptr.get()), &hs_integer_free_string); return os << static_cast<char *>(str.get()); } private: std::shared_ptr<void> ptr; }; #endif
では使ってみましょう。100!を計算し出力するプログラムです。GHCランタイムシステムの初期化と終了を忘れないように。
hs_integer_example.cpp
#include <iostream> #include <boost/phoenix/core.hpp> #include <boost/phoenix/operator.hpp> #include <boost/range/irange.hpp> #include <boost/range/numeric.hpp> #include <boost/scope_exit.hpp> #include "hs_integer.hpp" int main(int argc, char *argv[]) { // initialize and terminate GHC's runtime system hs_init(&argc, &argv); BOOST_SCOPE_EXIT(void) { hs_exit(); } BOOST_SCOPE_EXIT_END using namespace boost::phoenix::placeholders; std::cout << "100!=" << // hs_integer(1) * 1 * 2 * ... * 100 boost::accumulate(boost::irange(1, 101), hs_integer(1), _1 * _2) << std::endl; }
$ g++ -c -I/path/to/haskell-platform/lib/include hs_integer_example.cpp $ ghc -no-hs-main HsInteger_stub.o hs_integer_example.o -lstdc++ -o hs_integer_example $ ./hs_integer_example 100!=9332621544...
うまく動いています。
こんな感じで完全な多倍長整数クラスを作れると思います。なおネタなのでパフォーマンスとかは知りません。
おまけ
ついでにPythonでもやってみました。
py_integer.hpp
#ifndef PY_INTEGER_HPP #define PY_INTEGER_HPP #include <ostream> #include <string> #include <boost/lexical_cast.hpp> #include <boost/operators.hpp> #include <boost/python.hpp> class py_integer : public boost::multipliable<py_integer> { public: py_integer(int n = 0) : obj(n) {} py_integer &operator*=(py_integer const &rhs) { obj *= rhs.obj; return *this; } template <class Char, class CharTraits> friend std::basic_ostream<Char, CharTraits> & operator<<(std::basic_ostream<Char, CharTraits> &os, py_integer const &n) { return os << boost::python::extract<char const *>(boost::python::str(n.obj)); } private: boost::python::object obj; }; #endif
py_integer_example.cpp
#include <iostream> #include <boost/phoenix/core.hpp> #include <boost/phoenix/operator.hpp> #include <boost/range/irange.hpp> #include <boost/range/numeric.hpp> #include "py_integer.hpp" int main() { // initialize Python Py_Initialize(); using boost::phoenix::placeholders::_1; using boost::phoenix::placeholders::_2; std::cout << "100!=" << // py_integer(1) * 1 * 2 * ... * 100 boost::accumulate(boost::irange(1, 101), py_integer(1), _1 * _2) << std::endl; }
$ g++ py_integer_example.cpp -lpython27 -lboost_python -o py_integer_example $ ./py_integer_example 100!=9332621544...
boost::python::objectが優秀なので、これを軽く包むだけで実装できてしまいます。operator*=がオーバーロードされているあたりがすごい。
ここまで優秀だとboost::python::objectを直接使えばいい感じですが、せっかくC++なのでラップして型安全度を上げた、ということにしておきます。
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 などに対応しているという利点があったりもします。