I did a bit of reading around the topic of delegates after a brief discussion about signals and slots with Leeor.
Bjarne Stroustrup had a list of C++11 features, which included a section on
std::function. Of particular note is the section towards the end, it states a member function is treated the same as a free function with an extra argument (corresponding to
this). He called the function both explicitly passing
this, and using
std::bind to fix the
this pointer to a constant value, making it callable the same way as a free function:
struct X {
int foo(int);
};
function<int (X*, int)> f;
f = &X::foo; // pointer to member
X x;
int v = f(&x, 5); // call X::foo() for x with 5
function<int (int)> ff = std::bind(f,&x,_1); // first argument for f is &x
v=ff(5); // call x.foo(5)
Here's some reference info:
std::functionstd::bind
The
std::function type is general-purpose polymorphic function wrapper. It holds arbitrary references to callable types. What matters is the signature of the call, not the type of the underlying Callable target. It can be assigned from a free function, a member function, a member data variable, a functor (an object which overloads
operator()), and lambdas (which are really actually functors).
The
std::bind template creates a "forwarding call wrapper", which can fix argument values to some specified constant. This effectively reduces the number of arguments the new wrapped function now accepts. This is particularly useful for fixing the
this pointer for member functions.
auto twoParamFunction = std::bind(someFunction, 5, _1, _2);
twoParamFunction(x, y); // someFunction(5, x, y)
One oddity with
bind is the use of placeholder values
_1, _1, _3, .... This adds a bit of noise to the location where a method is bound, though does allow for some flexibility, such as rearranging argument order.
auto reversedParameters = std::bind(someFunction, _2, _1);
reversedParameters(x, y); // someFunction(y, x)
Putting this all together, here's how these components might be used to implement a click handler which could accept various types of callable objects:
#include <functional>
#include <iostream>
// Define handler type: a callable object returning nothing, and taking no parameters
using ClickHandler = std::function<void()>;
void freeFunction() {
std::cout << "freeFunction called" << std::endl;
}
struct S {
void method1() {
std::cout << "S::method1 called" << std::endl;
}
void method2() {
std::cout << "S::method2 called" << std::endl;
}
};
struct Functor {
void operator()() {
std::cout << "Function::operator() called" << std::endl;
}
};
int main() {
ClickHandler handler;
handler = freeFunction;
handler();
S s;
handler = std::bind(&S::method1, s);
handler();
handler = std::bind(&S::method2, s);
handler();
Functor functor;
handler = functor;
handler();
return 0;
}
If instead the click handler were to accept two arguments, an x and y location, update the following lines:
// Define handler type: a callable object returning nothing and taking two parameters
using ClickHandler = std::function<void(int, int)>;
void freeFunction(int x, int y) {
...
void method1(int x, int y) {
...
void method2(int x, int y) {
...
void operator()(int x, int y) {
...
handler(1, 2);
...
handler = std::bind(&S::method1, s, _1, _2);
handler(1, 2);
handler = std::bind(&S::method2, s, _1, _2);
handler(1, 2);
...
handler(1, 2);