Joachim Pileborg Joachim Pileborg - 1 month ago 20
C++ Question

Using std::bind with member function, use object pointer or not for this argument?

When using

std::bind
to bind a member function, the first argument is the objects
this
pointer. However it works passing the object both as a pointer and not.

See for example the following program:

#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();
}


Both clang and GCC compiles this without complaints, and the result works for both binds:


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


I have been trying to wrap my head around the specification (section 20.8.9) but it's one of the places where it's far from clear to me.

Should only one be correct, or are both correct?

Answer

Both are correct. 20.8.9.1.2 forwards to 20.8.2 to describe the requirements and the effect of your call to bind. 20.8.2 is:

20.8.2 Requirements [func.require]

1 Define INVOKE(f, t1, t2, ..., tN) as follows:

(t1.*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;

((*t1).*f)(t2, ..., tN) when f is a pointer to a member function of a class T and t1 is not one of the types described in the previous item;

t1.*f when N == 1 and f is a pointer to member data of a class T and t1 is an object of type T or a reference to an object of type T or a reference to an object of a type derived from T;

(*t1).*f when N == 1 and f is a pointer to member data of a class T and t1 is not one of the types described in the previous item;

f(t1, t2, ..., tN) in all other cases.

The first two options allow both a reference and a pointer.

The important thing to notice here is that the wording does not limit you to plain pointers. You could use a std::shared_ptr or some other smart pointer to keep your instance alive while bound and it would still work with std::bind as t1 is dereferenced, no matter what it is (given, of course, that it's possible).