20

たとえば、これは私のメンバー関数です ( do_it):

class oops
{
public:
    void do_it(GtkWidget *widget, GdkEvent *event, gpointer data)
    {
        g_print ("Hi there :)\n");
    }
};

...そして、私はstd::bindそれを非メンバー関数のように見せるために使用します:

oops o;
std::function<void(GtkWidget*, GdkEvent*, gpointer)> f = std::bind(&oops::do_it, o);

動作しません。次はコンパイラ エラー メッセージです。

program.cc: In function ‘int main(int, char**)’:
program.cc:69:85: error: conversion from ‘std::_Bind_helper<false, void (oops::*)(_GtkWidget*, _GdkEvent*, void*), oops&>::type {aka std::_Bind<std::_Mem_fn<void (oops::*)(_GtkWidget*, _GdkEvent*, void*)>(oops)>}’ to non-scalar type ‘std::function<void(_GtkWidget*, _GdkEvent*, void*)>’ requested
   std::function<void(GtkWidget*, GdkEvent*, gpointer)> f = std::bind(&oops::do_it, o);
                                                                                     ^

次を使用して修正する必要がありますstd::placeholders

oops o;
std::function<void(GtkWidget*, GdkEvent*, gpointer)> f = std::bind(&oops::do_it, o, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3);

を指定しないと動作しないのはなぜstd::placeholdersですか?

4

2 に答える 2

54

std::bind()関数への(部分的な)呼び出しを表す呼び出し可能なエンティティを作成するように設計されています。呼び出しのいくつかのパラメーターを生成された呼び出しオブジェクトにバインドし、呼び出しの時点で残りのパラメーターを指定できるようにします。

void f(int,int,int);

int main()
{
    std::function<void()> f_call = std::bind( f , 1 , 2 , 3);

    f_call(); //Equivalent to f(1,2,3)
}

の最初のパラメーターはstd::bind()呼び出される関数で、残りは呼び出しの引数です。

この例では、3 つのパラメーターすべてが指定された状態で呼び出しオブジェクトが生成されるため、呼び出しポイントにはパラメーターがありません。ここで、部分的に定義された呼び出しを考えてみましょう:

std::function<void(int,int,int)> f_call = std::bind( f );

関数には 3 つのパラメーターがあり、何も指定していないため、これはコンパイルされません。それは意味がありませんよね?3 つのパラメーターを持つ関数がある場合は、call オブジェクトに 3 つのパラメーターを渡す必要があります。

呼び出しポイントでいくつかのパラメーターを指定する必要があることを指定する必要がある場合は、プレースホルダーを使用してそのパラメーターを表す必要があります。例えば:

using namespace std::placeholders;

std::function<void(int,int,int)> f_call = std::bind( f , _1 , _2 , _3 );

f_call( 1 , 2 , 3 ); //Same as f(1,2,3)

ご覧のとおり、プレースホルダーを使用して、関数呼び出し用の 3 つの「スペース」、つまり呼び出しポイントで指定される 3 つのパラメーターを指定しました。プレースホルダーの番号は、呼び出しポイントでのパラメーターの番号を
指定することに注意してください。呼び出しポイントの最初のパラメーターは で識別され、2 番目のパラメーターは で識別されます。これは、さまざまな方法でパラメーターを指定したり、関数呼び出しのパラメーターを並べ替えたりするために使用できます。例:_1_2

std::function<void(int,int)> f_call = std::bind( f , _1 , 2 , _2 );

f_call( 1 , 3 ); //Equivalent to f( 1 , 2 , 3 );

std::function<void(int,int,int)> reordered_call = std::bind( f , _3 , _2 , _1 );

reordered_call( 3 , 2 , 1 ); //Same as f( 1 , 2 , 3 );

最後に、std::bind()メンバー関数を、それを呼び出すために使用されるオブジェクトにバインドするために使用できます。

struct foo
{
    void f() const;
};

int main()
{
    foo myfoo;
    std::function<void()> f = std::bind( &foo::f , std::cref( myfoo ) );

    f(); //Tah dah!
}

メンバー関数は、呼び出しが行われるオブジェクトである 1 つの隠しパラメーターを持つ関数と見なすことができます。そのため、オブジェクトは最初のパラメーターとしてバインドされます。

ただし、上記の例とまったく同じように、バインド ポイントで特定の数のパラメーターしかわかっておらず、後で呼び出しポイントで他のパラメーターを指定する必要がある場合は、 placeholders を使用する必要があります

using namespace std::placeholders;

oops o;

std::function<GtkWidget*,GtkEvent*,gpointer> do_it = std::bind( &oops::do_it , std::ref( o ) , _1 , _2 , _3 );

do_it( /* first param */ , /*second param */ , /* third param */ ); //Call

いくつかの詳細

呼び出しオブジェクトの署名

std::functioncall オブジェクトを格納するために使用することに注意してください。その関数のシグネチャは、生成された呼び出しオブジェクトのタイプ、つまりバインディング ポイントでパラメーターを指定した方法によって異なります

呼び出しオブジェクトは、元の関数への呼び出しとして機能する別の呼び出し可能なエンティティです。f()関数の例に従います。

std::function<void()> f_call = std:bind( f , 1 , 2 , 3 );

ここで call オブジェクトのシグネチャは ですvoid()。これは、バインディング ポイントでパラメーターのホール セットを指定したためであり、呼び出しポイントで指定する必要のあるものは残っていません (つまり、call オブジェクトにはパラメーターがありません)。

部分呼び出しの場合:

std::function<void(int,int,int)> f_call = std::bind( f, _1 , _2 , _3 );

f_call( 1 , 2 , 3 );

コール オブジェクトのシグネチャは です。これはvoid(int,int,int)、コール ポイントで指定する 3 つのパラメータが残っているためです (プレースホルダーに注意してください)。通常、呼び出しオブジェクトには、バインド ポイントで指定したプレースホルダーと同じ数のパラメーターがあります。.

于 2014-03-15T10:29:31.533 に答える