ファンクターとは何か、アルゴリズムでいつ使用するかは知っていますstd
が、Stroustrup がC++11 FAQでファンクターについて何を言っているのか理解できません。
誰が何std::bind
をstd::function
いつ使用すべきかを説明し、初心者向けの例をいくつか挙げることができますか?
ファンクターとは何か、アルゴリズムでいつ使用するかは知っていますstd
が、Stroustrup がC++11 FAQでファンクターについて何を言っているのか理解できません。
誰が何std::bind
をstd::function
いつ使用すべきかを説明し、初心者向けの例をいくつか挙げることができますか?
std::bind
は部分機能適用用です。
つまり、f
3 つの引数を取る関数オブジェクトがあるとします。
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);
これは、実際にファンクター クラスを作成して実行するよりも簡潔です。
リンク先の記事には、さらに例があります。ファンクターを何らかのアルゴリズムに渡す必要がある場合は、通常、これを使用します。必要な機能をほぼ実行する関数またはファンクターがありますが、アルゴリズムが使用するよりも構成可能です (つまり、より多くのパラメーターがあります)。したがって、引数をいくつかのパラメーターにバインドし、残りはアルゴリズムが入力できるようにします。
// 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 つのパラメーターを取り、任意の累乗に累乗できますが、重要なのは 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 は、boost bind を含める提案の後、ライブラリに投票されました。主に、いくつかのパラメーターを修正し、他のパラメーターをその場で変更できる部分的な関数の特殊化です。これは、C++ でラムダを実行するライブラリの方法です。スティーブ・ジェソップの回答
C++11 がラムダ関数をサポートするようになったので、std::bind を使用したいという誘惑はもうありません。ライブラリ機能よりもカリー化(部分特化)を言語機能で使いたい。
std::function オブジェクトは多相関数です。基本的な考え方は、呼び出し可能なすべてのオブジェクトを交換可能に参照できるようにすることです。
詳細については、次の 2 つのリンクを参照してください。
C++11 のラムダ関数: https://www.variadic.xyz/2011/10/12/c11-lambda-having-fun-with-brackets/
C++ の呼び出し可能エンティティ: https://www.variadic.xyz/2011/05/31/callable-entity/
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;
};
そして、binder3 クラスを使用するためのヘルパー関数 - 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/AC-Plug-in-ThreadPool-Design