Когда вы говорите «первый аргумент является ссылкой», вы наверняка хотели сказать «первый аргумент - это указатель»: оператор &
принимает адрес объекта, давая указатель.
Прежде чем ответить на этот вопрос, давайте ненадолго вернемся назад и посмотрим на ваше первое использование 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
, но его можно использовать с любым объектом этого класса. То есть он не зависит от какого-либо конкретного объекта. В C ++ нет прямого способа получить функцию-член, напрямую привязанную к объекту (я думаю, что в C # вы можете получать функции, напрямую связанные с объектом, используя объект с примененным именем члена; однако прошло более 10 лет с тех пор, как В последний раз я немного программировал на C #).
Внутри 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()
был вызван для соответствующих объектов (семантическое поведение такое же, но компилятору, вероятно, будет намного сложнее встраивать функции и обычно он не работает. делает это, когда задействованы указатели на члены).
person
Dietmar Kühl
schedule
05.06.2016
&
, за которым следует идентификатор, не означает ссылку. Нет, если только он не объявляет переменную, и в этом случае&
будет предшествовать typename. Когда&
применяется к идентификатору, вы получаете указатель. - person Nicol Bolas   schedule 05.06.2016std::bind
. У него есть действительно глубокие причуды, благодаря которым он дает удивительные результаты даже после того, как вы овладеете основами. Я считаю, что оно того не стоит. - person Yakk - Adam Nevraumont   schedule 05.06.2016