私は実際、遅延呼び出しファンクターを作成する小さなユーティリティを作成しました(多少std::bind
似ていますが、ネストされたバインド式/プレースホルダー機能はありません)。私の主な動機は、直感に反していると感じたこのケースでした。
using pointer_type = std::unique_ptr<int>;
pointer_type source();
void sink(pointer_type p);
pointer_type p = source();
// Either not valid now or later when calling bound()
// auto bound = std::bind(sink, std::move(p));
auto bound = std::bind(
[](pointer_type& p) { sink(std::move(p)); }
, std::move(p) );
bound();
そのアダプタ(左辺値のref引数をに移動するsink
)の理由は、呼び出しラッパーがstd::bind
常にバインドされた引数を左辺値として転送するためです。これは、たとえばC ++ 03では問題ではありませんでした。これはboost::bind
、その左辺値が、基になるCallableオブジェクトの参照引数にバインドされるか、コピーを介して値引数にバインドされるためです。pointer_type
は移動専用なので、ここでは機能しません。
私が得た洞察は、バインドされた引数を格納する方法と、それらを復元する方法(つまり、Callableオブジェクトに渡す方法)の2つを考慮する必要があるということです。付与するコントロールstd::bind
は次のとおりです。引数は、浅いstd::ref
方法(を使用して)または通常の方法(std::decay
完全な順方向で使用)のいずれかで格納されます。それらは常に左辺値として復元されます(所有する呼び出しラッパーから継承されたcv修飾子を使用)。私が行ったように、小さなオンサイトアダプターラムダ式で後者をバイパスできることを除いて。
おそらく、学ぶことは比較的少ないので、多くの制御と多くの表現です。比較すると、私のユーティリティには、bind(f, p)
(減衰してコピーを保存、左辺値として復元)、bind(f, ref(p))
(浅く保存、左辺値として復元)、bind(f, std::move(p))
(減衰して移動から保存、右辺値として復元)、bind(f, emplace(p))
(減衰して移動から保存、左辺値として復元)などのセマンティクスがあります。これは、EDSLを学ぶようなものです。