In my previous post about delegates I wrote how to bind a member function to std::function
. There was a lot of template magic involved in order to pass the correct number of placeholders. Shortly after I published I received a lot of feedback and I’m very grateful for this. One advice was not to use std::bind()
at all.
I’ll paste directly the code from one of the comments and will make some time measurements:
template <class T, class R, class ... Args>
auto make_function(T t, R(T::*mem_fn)(Args...))
{
return [t, mem_fn] (Args ... args) mutable -> R { return ((t).*(mem_fn))(args...); };
}
As you can see - it’s usual lambda that can be converted to std::function
.
My first thoughts were that lambda should be slower - we have a function call and a lambda call over it. To be honest, I don’t know how std::bind
works, maybe it also creates some wrappers? Anyway, let’s benchmark:
struct S
{
int foo(int a, int b)
{
return a + b;
}
};
int main()
{
const uint32_t NUM_TESTS{ 10 };
const uint32_t NUM_ITERATIONS{ 100000000 };
volatile int res{ 0 };
S s;
function<int(int, int)> f{ bind(&S::foo, &s, placeholders::_1, placeholders::_2) }; // passing an object's pointer as I did in previous article
{
uint64_t t{ 0 };
for (int i{ 0 }; i < NUM_TESTS; ++i)
{
auto start = chrono::high_resolution_clock::now();
for (int j{ 0 }; j < NUM_ITERATIONS; ++j)
{
res += f(5, 10);
}
auto end = chrono::high_resolution_clock::now();
auto time = chrono::duration_cast<chrono::milliseconds>(end - start);
t += time.count();
}
cout << t / NUM_TESTS << "\n";
}
auto* ptr = &s;
auto fPtr = &S::foo;
function<int(int, int)> f2 = [ptr, fPtr](int a, int b){ return ((ptr)->*(fPtr))(a, b); }; // capture pointers by copy
res = 0;
{
uint64_t t{ 0 };
for (int i{ 0 }; i < NUM_TESTS; ++i)
{
auto start = chrono::high_resolution_clock::now();
for (int j{ 0 }; j < NUM_ITERATIONS; ++j)
{
res += f2(5, 10);
}
auto end = chrono::high_resolution_clock::now();
auto time = chrono::duration_cast<chrono::milliseconds>(end - start);
t += time.count();
}
cout << t / NUM_TESTS << "\n";
}
cin.get();
return 0;
}
On my machine I have around 400ms
for both std::bind()
and lambda. Not bad. My previous code can be simplified dramatically now - there’s no need in std::bind()
and accompanying template tricks.