Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

C++11 Optimization of empty function passed as a callback

I have a function template:

template <class ReportFunc>
void func (ReportFunc report_func)
{
    for (/* ... */)
    {
         do_something (a, b);
         report_func (a, b, c);
         do_something_else (b, c);
    }
}

It is sometimes desirable to call func() without any ReportFunc, i.e. the loop just calls do_something() and do_something_else() and nothing else. If I write an overload of f() which doesn't take a ReportFunc parameter, I'll have to duplicate f()'s implementation code, just removing the line which calls report_func().

I have several functions of this kind - sometimes I want to call them with a ReportFunc, sometimes without it. So I'd like to avoid all the code duplication. If I pass an empty lambda or void or something like this, should it make a C++11 compiler generate an instantiation of f() which doesn't call any report_func()? And is it as fast as simply deleting the line which calls report_func() or even an empty lambda has some overhead the compiler doesn't optimize? (in my specific case, I use GCC)

Also: if an empty lambda indeed does that, and I change the function f()'s return type to be ReportFunc, i.e. it returns the report_func argument, is it still safe to store the returned value in a variable and call it? (even if it's an empty lambda? so calling it is theoretically possible, it simply means nothing really happens)

like image 965
cfa45ca55111016ee9269f0a52e771 Avatar asked Jan 15 '23 00:01

cfa45ca55111016ee9269f0a52e771


1 Answers

Just pass an empty functor.

As long as you have optimization turned on the compiler will instantiate the template, inline the (empty) call to the functor, and so do nothing. It should optimize away to nothing, don't bother with metaprogramming to try and remove the call.

I won't swear that G++ optimizes away a "do nothing" lambda in the same way, but it should do, because the type is known and its function call operator is inline and known to be empty.

Using a lambda has no inherent overhead, it's just syntactic sugar for declaring an object type with an operator() and creating a temporary of that type. There's quite a lot of work needed for the compiler front-end to do all that, but once the type exists the optimizer should treat it exactly the same as a user-defined struct that does the same thing. For that reason it's safe to return it too, it's just an instance of an object type, like a user-defined function object.

like image 77
Jonathan Wakely Avatar answered Jan 22 '23 09:01

Jonathan Wakely