What is the best way of passing a callback function parameter in C++?
I thought of simply using templates, like this:
template <typename Function>
void DoSomething(Function callback)
This is the way used e.g. in std::sort
for the comparison function object.
What about passing using &&
? E.g.:
template <typename Function>
void DoSomething(Function&& callback)
What are the pros and cons of those two methods, and why does the STL uses the former e.g. in std::sort
?
The
template <typename Function>
void DoSomething(Function&& callback)
… which uses a forwarding reference to pass by reference, is IMHO superior for the case where the function just uses the callback. Because a functor object, although usually small, can be arbitrarily large. Passing it by value then incurs some needless overhead.
The formal argument can end up as T&
, T const&
or T&&
depending on the actual argument.
On the other hand, passing a simple function pointer by reference involves an extra needless indirection, which in principle could mean some slight overhead.
If in doubt whether this is significant for a given compiler, system and application, then measure.
Regarding
” why does the STL uses the former [passing by value] e.g. in
std::sort
?
… it's worth noting that std::sort
has been there since C++98, well before forwarding references were introduced in C++11, so it could not have that signature originally.
Possibly there just has not been sufficient incentive to improve this. After all, one should generally not fix that which works. Still, extra oveloads with an “execution policy” argument are introduced in C++17.
Since this concerns a possible C++11 change, it's not covered by the usual source for rationales, namely Bjarne Stroustrup's “The design and evolution of C++”., and I do not know of any conclusive answer.
Using the template<class F> void call( F )
style, you can still get back "pass by reference". Simply do call( std::ref( your callback ) )
. std::ref
overrides operator()
and forwards it to the contained object.
Simularly, with template<class F> void call( F&& )
style, you can write:
template<class T>
std::decay_t<T> copy( T&& t ) { return std::forward<T>(t); }
which explicitly copies something, and force call
to use a local copy of f
by:
call( copy(f) );
so the two styles mainly differ in how they behave by default.
Since this is C++11 (or higher): <functional>
and std::function
are your best friends here.
#include <iostream>
#include <functional>
#include <string>
void DoSomething(std::function<void()> callback) {
callback();
}
void PrintSomething() {
std::cout << "Hello!" << std::endl;
}
int main()
{
DoSomething(PrintSomething);
DoSomething([]() { std::cout << "Hello again!" << std::endl; });
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With