Используя std::bind с функцией-членом, использовать указатель объекта или нет для этого аргумента?
Когда используешь std::bind
чтобы связать функцию-член, первым аргументом являются объекты this
указатель. Однако он работает, передавая объект как указатель, так и нет.
Смотрите, например, следующую программу:
#include <iostream>
#include <functional>
struct foo
{
void bar(int v) { std::cout << "foo::bar - " << v << '\n'; }
};
int main()
{
foo my_foo;
auto f1 = std::bind(&foo::bar, my_foo, 1);
auto f2 = std::bind(&foo::bar, &my_foo, 2);
f1();
f2();
}
И clang, и GCC компилируют это без жалоб, и результат работает для обоих связей:
foo:: bar - 1 foo:: bar - 2
Я пытался обернуть голову вокруг спецификации (раздел 20.8.9), но это одно из тех мест, где оно мне далеко не ясно.
Должен ли быть верным только один или оба?
3 ответа
Оба верны. 20.8.9.1.2 переадресация на 20.8.2 для описания требований и последствий вашего звонка bind
, 20.8.2 это:
20.8.2 Требования [func.require]
1 Определите INVOKE
(f, t1, t2, ..., tN)
следующее:-
(t1.*f)(t2, ..., tN)
когдаf
указатель на функцию-член классаT
а такжеt1
является объектом типаT
или ссылка на объект типаT
или ссылка на объект типа, производного отT
;-
((*t1).*f)(t2, ..., tN)
когдаf
указатель на функцию-член классаT
а такжеt1
не является одним из типов, описанных в предыдущем пункте;-
t1.*f
когдаN == 1
а такжеf
указатель на данные члена классаT
а такжеt1
является объектом типаT
или ссылка на объект типаT
или ссылка на объект типа, производного отT
;-
(*t1).*f
когдаN == 1
а такжеf
указатель на данные члена классаT
а такжеt1
не является одним из типов, описанных в предыдущем пункте;-
f(t1, t2, ..., tN)
во всех остальных случаях.
Первые два параметра позволяют использовать как ссылку, так и указатель.
Здесь важно отметить, что формулировка не ограничивает вас простыми указателями. Вы могли бы использовать std::shared_ptr
или какой-то другой умный указатель, чтобы сохранить ваш экземпляр живым, пока он связан, и он все равно будет работать с std::bind
как t1
разыменовывается, независимо от того, что это (учитывая, конечно, что это возможно).
Добавить к правильному ответу (что разрешены обе формы).
Я думаю о двух вариантах привязки по аналогии с объявлением аргумента функции, которое может быть "передано по значению" или "передано по ссылке".
В случае f1
(иначе прохождение my_foo
"по значению") результат не "видит" никаких изменений, внесенных в my_foo
мимо обязательной точки. Это может быть нежелательно, особенно если my_foo
эволюционирует. Привязка "По значению" имеет дополнительную "стоимость" (нескольких) обращений к конструктору копирования.
Есть разница. Как указал Ритис, передача по значению не приводит к изменениям, внесенным в my_foo. Например, в случае, когда my_foo является классом, передача по значению не приводит к изменению данных, внесенных в данные члена my_foo.