Используя 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 компилируют это без жалоб, и результат работает для обоих binds:

foo::bar - 1
foo::bar - 2

Я пытаюсь обвести голову вокруг спецификации (раздел 20.8.9), но это одно из мест, где это далеко не ясно для меня.

Должен ли только один быть правильным или оба правильны?

Ответ 1

Оба правильные. 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 разыменовывается независимо от того, что это (дано, конечно, возможно).

Ответ 2

Чтобы добавить к правильному ответу (что обе формы разрешены).

Я думаю о двух вариантах привязки по аналогии с объявлением аргумента функции, которые могут быть "переданы по значению" или "переданы по ссылке".

В случае f1 (также передавая my_foo "по значению" ) результат не "видит" любые изменения, сделанные в my_foo за точкой привязки. Это может быть нежелательно, особенно если my_foo развивается. "По значению" привязка имеет дополнительную "стоимость" (нескольких) вызовов конструктора копирования.

Ответ 3

Есть разница. Поскольку rytis выдвигается, передача по значению не видит изменений, внесенных в my_foo. Например, в случае, если my_foo является классом, передача по значению не видит изменений, внесенных в данные элемента my_foo.