I noticed that lambdas both work using function pointers as well as the dedicated function
type using g++
.
#include <functional>
typedef int(*fptr)(int, int);
int apply_p(fptr f, int a, int b) {
return f(a, b);
}
int apply_f(std::function<int(int, int)> f, int a, int b) {
return f(a, b);
}
void example() {
auto add = [](int a, int b) -> int {
return a + b;
};
apply_p(add, 2, 3); // doesn't give an error like I'd expect it to
apply_f(add, 2, 3);
}
My question is: Which of these are most idiomatic to use? And what are the dangers and/or benefits of using one over the other?
I noticed that lambdas both work using function pointers as well as the dedicated
function
type
If the lambda doesn't capture anything, then it can decay to a function pointer.
Which of these are most idiomatic to use?
Neither. Use function
if you want to store an arbitrary callable object. If you just want to create and use one, keep it generic:
template <typename Function>
int apply(Function && f, int a, int b) {
return f(a,b);
}
You can go further and make the return and argument type(s) generic; I'll leave that as an exercise.
And what are the dangers and/or benefits of using one over the other?
The function pointer version only works with (non-member or static) functions and non-capturing lambdas, and doesn't allow any state to be passed; only the function itself and its arguments. The function
type can wrap any callable object type, with or without state, so is more generally useful. However, this has a run-time cost: to hide the wrapped type, calling it will involve a virtual function call (or similar); and it may need dynamic memory allocation to hold a large type.
I would say (C++14)
template <class Functor, class... Args>
decltype(auto) apply_functor(Functor&& f, Args&&... args) {
return std::forward<Functor>(f)(std::forward<Args>(args)...);
}
or C++11:
template <class Functor, class... Args>
auto apply_functor(Functor&& f, Args&&... args) ->decltype(std::forward<Functor>(f)(std::forward<Args>(args)...)) {
return std::forward<Functor>(f)(std::forward<Args>(args)...);
}
If in doubt, prefer std::function
:
The syntax is much more straightforward and consistent with other types, consider e.g.
typedef std::function<int (int, int)> MyAddFunction;
and
typedef int (*MyAddFunction)( int, int );
It's more generic (it actually can take a lambda or a plain C function or a functor)
It's more type safe
apply_p(0, 0, 0); // probably crashes at runtime, dereferences null pointer
apply_f(0, 0, 0); // doesn't even compile
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