Ответ 1
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()
является вызываемая функция, а остальные - аргументы вызова.
В этом примере объект вызова генерируется со всеми указанными тремя параметрами, поэтому точка вызова не имеет параметров. Теперь рассмотрим частично определенный вызов:
std::function<void(int,int,int)> f_call = std::bind( f );
Это не скомпилируется, потому что функция имеет три параметра, и вы никого не указали! Это не имеет смысла, правильно? Если у вас есть функция с тремя параметрами, вы должны передать три параметра объекту вызова.
Если вам нужно указать, что некоторые параметры должны быть указаны в точке вызова, вы должны использовать заполнители для представления этих параметров. Например:
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)
Как вы можете видеть, мы использовали заполнители, чтобы указать три "пробела" для вызова функции, т.е. три параметра, которые будут указаны в точке вызова.
Обратите внимание, что числа заполнителей указывают номер параметра в точке вызова. Первый параметр точки вызова идентифицируется _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!
}
Функция-член может рассматриваться как функция с одним скрытым параметром, который является объектом, с которым выполняется вызов. Вот почему объект привязан как первый параметр.
Но, как и в приведенных выше примерах, , если вы знаете только определенное количество параметров в точке привязки и должны указывать другие позже в точке вызова, вы должны использовать заполнители:
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::function
для хранения объекта вызова. Подпись этой функции зависит от типа генерируемого объекта вызова, то есть зависит от того, как вы указали параметры в точке привязки.
Объект вызова - это еще один вызываемый объект, который действует как вызов исходной функции. Следуя нашим f()
примерам функций:
std::function<void()> f_call = std:bind( f , 1 , 2 , 3 );
Здесь подпись объекта вызова void()
, потому что мы указали набор отверстий параметров в точке привязки, и никто не должен быть указан в точке вызова (поэтому объект вызова не имеет параметров).
В случае частичного вызова:
std::function<void(int,int,int)> f_call = std::bind( f, _1 , _2 , _3 );
f_call( 1 , 2 , 3 );
подпись объекта вызова void(int,int,int)
, потому что мы оставили три параметра, которые должны быть указаны в точке вызова (обратите внимание на заполнители). В общем случае объект вызова имеет такое же количество параметров, что и заполнители, которые вы указали в точке привязки..