ファンクタとは何か、std
アルゴリズムでそれらを使用するタイミングは知っていますが、Stroustrupが C++ 11 FAQ でそれらについて言っていることを理解していません。
誰がstd::bind
とstd::function
がいつ使用されるべきかを説明し、初心者向けの例を提供できますか?
std::bind
は、 部分関数アプリケーション 用です。
つまり、3つの引数を取る関数オブジェクトf
があるとします。
f(a,b,c);
次のように定義された、2つの引数のみを取る新しい関数オブジェクトが必要です。
g(a,b) := f(a, 4, b);
g
は、関数の「部分的なアプリケーション」ですf
:中央の引数は既に指定されており、残り2つあります。
std::bind
を使用してg
を取得できます。
auto g = bind(f, _1, 4, _2);
これは、実際にファンクタークラスを作成してそれを行うよりも簡潔です。
リンク先の記事にはさらに例があります。通常、ファンクターを何らかのアルゴリズムに渡す必要がある場合に使用します。あなたはalmostが望む仕事をする関数またはファンクターを持っていますが、アルゴリズムが使用するよりも設定可能です(つまり、より多くのパラメーターを持っています)。そのため、引数をいくつかのパラメーターにバインドし、残りはアルゴリズムが入力できるようにします。
// raise every value in vec to the power of 7
std::transform(vec.begin(), vec.end(), some_output, std::bind(std::pow, _1, 7));
ここで、pow
は2つのパラメーターを取り、any乗することができますが、気にするのは7の累乗です。
部分的な関数アプリケーションではない時折の使用として、bind
は関数への引数を並べ替えることもできます。
auto memcpy_with_the_parameters_in_the_right_flipping_order = bind(memcpy, _2, _1, _3);
APIが気に入らないという理由だけで使用することはお勧めしませんが、次の理由から実用的な用途がある可能性があります。
not2(bind(less<T>, _2, _1));
は、以下の関数です(全順序、なんとか何と仮定)。すでにstd::less_equal
が存在するため、この例は通常必要ありません(<=
ではなく<
演算子を使用します。したがって、一貫性がなければ、これが必要になる可能性があります。また、手がかりを持ってクラスの作成者を訪問する必要があります)。ただし、機能的なスタイルのプログラミングを使用している場合は、一種の変換です。
std :: bindは、ブーストバインドを含めることを提案した後、ライブラリに投票されました。主に、一部のパラメーターを修正し、他のパラメーターをオンザフライで変更できる部分的な関数の特殊化です。これは、C++でラムダを実行するライブラリの方法です。スティーブ・ジェソップの回答通り
C++ 11がラムダ関数をサポートするようになったので、もうstd :: bindを使用する誘惑は感じません。むしろ、ライブラリ機能よりも言語機能でカリー化(部分的な専門化)を使用します。
std :: functionオブジェクトはポリモーフィック関数です。基本的な考え方は、すべての呼び出し可能オブジェクトを互換的に参照できるようにすることです。
詳細については、次の2つのリンクを参照してください。
C++ 11のLambda関数: http://www.nullptr.me/2011/10/12/c11-lambda-having-fun-with-brackets/#.UJmXu8XA9Z8
C++の呼び出し可能エンティティ: http://www.nullptr.me/2011/05/31/callable-entity/#.UJmXuMXA9Z8
Std :: functionとstd :: bindの主な用途の1つは、安全な関数ポインターとしてです。これを使用して、コールバックメカニズムを実装できます。人気のあるシナリオの1つは、実行に時間がかかるいくつかの関数があるが、それが戻るのを待ちたくない場合は、別のスレッドでその関数を実行し、それに関数ポインタを与えることができます完了後のコールバック。
これを使用する方法のサンプルコードを次に示します。
class MyClass {
private:
//just shorthand to avoid long typing
typedef std::function<void (float result)> TCallback;
//this function takes long time
void longRunningFunction(TCallback callback)
{
//do some long running task
//...
//callback to return result
callback(result);
}
//this function gets called by longRunningFunction after its done
void afterCompleteCallback(float result)
{
std::cout << result;
}
public:
int longRunningFunctionAsync()
{
//create callback - this equivalent of safe function pointer
auto callback = std::bind(&MyClass::afterCompleteCallback,
this, std::placeholders::_1);
//normally you want to start below function on seprate thread,
//but for illustration we will just do simple call
longRunningFunction(callback);
}
};
C++でプラグインスレッドプールを作成するのに長い時間を費やしました。関数は3つのパラメーターを受け取っていたので、このように書くことができます
メソッドに署名があるとします:
int CTask::ThreeParameterTask(int par1, int par2, int par3)
3つのパラメーターをバインドする関数オブジェクトを作成するには、次のようにします
// a template class for converting a member function of the type int function(int,int,int)
//to be called as a function object
template<typename _Ret,typename _Class,typename _arg1,typename _arg2,typename _arg3>
class mem_fun3_t
{
public:
explicit mem_fun3_t(_Ret (_Class::*_Pm)(_arg1,_arg2,_arg3))
:m_Ptr(_Pm) //okay here we store the member function pointer for later use
{}
//this operator call comes from the bind method
_Ret operator()(_Class *_P, _arg1 arg1, _arg2 arg2, _arg3 arg3) const
{
return ((_P->*m_Ptr)(arg1,arg2,arg3));
}
private:
_Ret (_Class::*m_Ptr)(_arg1,_arg2,_arg3);// method pointer signature
};
ここで、パラメーターをバインドするために、バインダー関数を作成する必要があります。だから、ここに行く:
template<typename _Func,typename _Ptr,typename _arg1,typename _arg2,typename _arg3>
class binder3
{
public:
//This is the constructor that does the binding part
binder3(_Func fn,_Ptr ptr,_arg1 i,_arg2 j,_arg3 k)
:m_ptr(ptr),m_fn(fn),m1(i),m2(j),m3(k){}
//and this is the function object
void operator()() const
{
m_fn(m_ptr,m1,m2,m3);//that calls the operator
}
private:
_Ptr m_ptr;
_Func m_fn;
_arg1 m1; _arg2 m2; _arg3 m3;
};
そして、バインダー3クラスを使用するヘルパー関数-bind3:
//a helper function to call binder3
template <typename _Func, typename _P1,typename _arg1,typename _arg2,typename _arg3>
binder3<_Func, _P1, _arg1, _arg2, _arg3> bind3(_Func func, _P1 p1,_arg1 i,_arg2 j,_arg3 k)
{
return binder3<_Func, _P1, _arg1, _arg2, _arg3> (func, p1,i,j,k);
}
そして、ここでそれを呼び出す方法
F3 f3 = PluginThreadPool::bind3( PluginThreadPool::mem_fun3(
&CTask::ThreeParameterTask), task1,2122,23 );
注:f3();メソッドtask1-> ThreeParameterTask(21,22,23)を呼び出します。
詳細な詳細については-> http://www.codeproject.com/Articles/26078/A-C-Plug-in-ThreadPool-Design