std :: functionとstd :: bind:それらは何で、いつ使うべきですか?


127

ファンクタとは何か、stdアルゴリズムでいつ使用するかは知っていますが、C ++ 11 FAQでStroustrupがファンクタについて何を言っているのか理解していません。

誰が何をどのように使用すべきかstd::bindを説明し、std::function初心者のためにいくつかの例を挙げられますか?

回答:


200

std::bind以下のためにある部分関数アプリケーション

つまり、f3つの引数を取る関数オブジェクトがあるとします。

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(これはでは<=なく演算子を使用します<。したがって、これらが一貫していない場合、これが必要になる場合があり、手がかりでクラスの作成者にアクセスする必要がある場合もあります)。ただし、関数型プログラミングを使用している場合は、このような変換が行われます。


17
また、便利なメンバ関数へのコールバックのために:myThread=boost::thread(boost::bind(&MyClass::threadMain, this))
rlduffy

15
バインドの素晴らしい説明。しかし、どうstd::functionですか?
RedX 2012

10
あなたのpow例はコンパイルされません。powはオーバーロードされた関数なので、どのオーバーロードを手動で指定する必要があります。バインディングは、結果のファンクタの呼び出し元によって推測されるようにすることはできません。例:2015std::transform(vec.begin(), vec.end(), out.begin(), std::bind((double (*)(double, int))std::pow, _1, 7));
MM

2
非常によく説明さstd::bindれていthisますが、2番目の引数として使用法と一緒になることもあります。このユースケースについて詳しく説明していただけますか?
メンデス、2016年

2
また、「_ 1」とはを意味しstd::placeholders::_1ます。これがコンパイルされなかった理由を見つけるために、しばらく時間をかけました。
terryg

24

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);
    }
};

5
これは素晴らしい答えです。私はこの答えを見つけるためにすべてを見回しました。ありがとう@ShitalShah
terryg 2018

バインディングが安全にするのに役立つ理由を説明してください。
Steven Lu

私の悪いことは…もっと「安全」だとは言いませんでした。通常の関数ポインタはまた、タイプセーフしかし、はstdている::機能はラムダとの仕事に、より汎用的で、コンテキストキャプチャ、メンバー・メソッドなど
Shitalシャー

bind(&MyClass :: afterCompleteCallback、this、std :: placeholders :: _ 1)、1の定義で2つの引数、void afterCompleteCallback(float result)、これを説明できますか?
ノック

1
@nonockメンバー関数の関数ポインターの場合、最初の引数として「this」ポインターを渡す必要があります。
sanojサブラン

12

std :: bindは、boost bindを含めるよう提案された後、ライブラリに投票されました。主に、部分的な関数の特殊化であり、いくつかのパラメータを修正して、他のパラメータをオンザフライで変更できます。これがC ++でラムダを実行するライブラリの方法です。Steve Jessopの回答

C ++ 11がラムダ関数をサポートするようになったので、std :: bindを使用する誘惑を感じなくなりました。ライブラリー機能ではなく、言語機能でカリー化(部分的な専門化)を使用したい。

std :: functionオブジェクトはポリモーフィック関数です。基本的な考え方は、すべての呼び出し可能なオブジェクトを交換可能に参照できるようにすることです。

詳細については、次の2つのリンクを参照してください。

C ++ 11のラムダ関数: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


5
std::bindラムダなしでは存在しませんでした-これらの機能は両方ともC ++ 11で導入されました。私たちは持っていなかったbind1stし、bind2ndこれはC ++ 11バインドのバージョンが衰弱しました。
MM

5

私は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

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.