Std:: bind функции члена класса

У меня есть этот код:

#include <iostream>
#include <functional>

struct Foo
{
        int get(int n) { return 5+n; }
};

int main()
{
        Foo foo;
        auto L = std::bind(&Foo::get, &foo, 3);

        std::cout << L() << std::endl;

        return 0;
}

Кажется, что:

auto L = std::bind(&Foo::get, &foo, 3);

эквивалентно:

auto L = std::bind(&Foo::get, foo, 3);

Почему?

Ответ 1

std::bind() принимает свои аргументы по значению. Это означает, что в первом случае вы передаете указатель по значению, в результате чего получается копия указателя. Во втором случае вы передаете объект типа foo по значению, в результате чего получается копия объекта типа Foo.

Как следствие, во втором случае вычисление выражения L() вызывает функцию-член get() для копии исходного объекта foo, который может быть, а может и не быть тем, что вы хотите.

Этот пример иллюстрирует разницу (забудьте о нарушении правила трех/правила пяти, это только для иллюстрации):

#include <iostream>
#include <functional>

struct Foo
{
    int _x;

    Foo(int x) : _x(x) { }

    Foo(Foo const& f) : _x(f._x)
    {
        std::cout << "Foo(Foo const&)" << std::endl;
    }

    int get(int n) { return _x + n; }
};

int main()
{
   Foo foo1(42);

   std::cout << "=== FIRST CALL ===" << std::endl;
   auto L1 = std::bind(&Foo::get, foo1, 3);
   foo1._x = 1729;
   std::cout << L1() << std::endl; // Prints 45

   Foo foo2(42);

   std::cout << "=== SECOND CALL ===" << std::endl;
   auto L2 = std::bind(&Foo::get, &foo2, 3);
   foo2._x = 1729;
   std::cout << L2() << std::endl; // Prints 1732
}

Живой пример.

Если по какой-либо причине вы не хотите использовать форму указателя, вы можете использовать std::ref(), чтобы предотвратить создание копии аргумента:

auto L = std::bind(&Foo::get, std::ref(foo), 3);

Ответ 2

Они не совпадают. Обозначение общей функции std::bind копирует его аргументы. В случае std::bind(&Foo::get,&foo,3) указатель копируется, но когда вы вызываете связанный объект, он по-прежнему применяется к исходному объекту foo. В std::bind(&Foo::get,foo,3) объект foo копируется, а более поздний вызов применяется к связанной копии, а не к исходному объекту.

Вы можете проверить это, используя функцию-член, которая обращается к внутреннему состоянию объекта, связывает объект в обоих направлениях, изменяет исходный объект и видит, как отличаются результаты.