Ответ 1
Когда вы говорите: "первый аргумент - это ссылка", вы наверняка хотели сказать "первый аргумент - это указатель": оператор &
принимает адрес объекта, уступая указателю.
Прежде чем отвечать на этот вопрос, давайте кратко отступим и посмотрим на ваше первое использование std::bind()
при использовании
std::bind(my_divide, 2, 2)
вы предоставляете функцию. Когда функция передается где угодно, она распадается на указатель. Вышеприведенное выражение эквивалентно этому, явно принимая адрес
std::bind(&my_divide, 2, 2)
Первый аргумент std::bind()
- это объект, определяющий способ вызова функции. В приведенном выше случае это указатель на функцию с типом double(*)(double, double)
. Любой другой вызываемый объект с подходящим оператором вызова функции тоже будет делать.
Так как функции-члены довольно распространены, std::bind()
предоставляет поддержку для работы с указателем на функции-члены. Когда вы используете &print_sum
, вы просто получаете указатель на функцию-член, т.е. Объект типа void (Foo::*)(int, int)
. В то время как имена функций неявно распадаются на указатели на функции, т.е. &
можно опустить, то же самое не верно для функций-членов (или элементов данных, если на то пошло): для получения указателя на функцию-член необходимо используйте &
.
Обратите внимание, что указатель на элемент специфичен для class
, но он может использоваться с любым объектом этого класса. То есть, он не зависит от какого-либо конкретного объекта. С++ не имеет прямого способа получить функцию-член, напрямую связанную с объектом (я думаю, что на С# вы можете получить функции, напрямую связанные с объектом, с использованием объекта с именем прикладного участника, однако это уже 10+ лет Я последний раз запрограммировал немного С#).
Внутри std::bind()
обнаруживает, что указатель на функцию-член передан и, скорее всего, превращает его в вызываемые объекты, например, используя std::mem_fn()
с его первым аргументом. Поскольку функция-член не static
нуждается в объекте, первый аргумент вызываемого объекта разрешения является либо ссылочным, либо [умным] указателем на объект соответствующего класса.
Чтобы использовать указатель на функцию-член, необходим объект. При использовании указателя на член с std::bind()
второй аргумент std::bind()
соответственно должен указывать, когда объект идет. В вашем примере
std::bind(&Foo::print_sum, &foo, 95, _1)
в результате вызываемого объекта используется &foo
, то есть указатель на foo
(типа Foo*
) в качестве объекта. std::bind()
достаточно умен, чтобы использовать что-либо, похожее на указатель, все, что может быть конвертировано в ссылку соответствующего типа (например, std::reference_wrapper<Foo>
) или [копировать] объекта в качестве объекта, когда первым аргументом является указатель на член.
Я подозреваю, вы никогда не видели указателя на члена - иначе было бы совершенно ясно. Вот простой пример:
#include <iostream>
struct Foo {
int value;
void f() { std::cout << "f(" << this->value << ")\n"; }
void g() { std::cout << "g(" << this->value << ")\n"; }
};
void apply(Foo* foo1, Foo* foo2, void (Foo::*fun)()) {
(foo1->*fun)(); // call fun on the object foo1
(foo2->*fun)(); // call fun on the object foo2
}
int main() {
Foo foo1{1};
Foo foo2{2};
apply(&foo1, &foo2, &Foo::f);
apply(&foo1, &foo2, &Foo::g);
}
Функция apply()
просто получает два указателя на объекты foo
и указатель на функцию-член. Он вызывает функцию-член, указанную с каждым из объектов. Этот забавный оператор ->*
применяет указатель на элемент к указателю на объект. Существует также оператор .*
, который применяет указатель на элемент к объекту (или, как они ведут себя как объекты, ссылку на объект). Поскольку указатель на функцию-член нуждается в объекте, необходимо использовать этот оператор, который запрашивает объект. Внутри std::bind()
организует то же самое.
Когда apply()
вызывается с двумя указателями и &Foo::f
, он ведет себя точно так же, как если бы член f()
вызывался на соответствующие объекты. Аналогично при вызове apply()
с двумя указателями и &Foo::g
он ведет себя точно так же, как если бы член g()
вызывался на соответствующие объекты (семантическое поведение такое же, но у компилятора, вероятно, будет намного сложнее и обычно отказывается делать это, когда задействованы указатели на элементы).