Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Which way of invoking lambdas in C++ is the most idiomatic?

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?

like image 835
Electric Coffee Avatar asked Nov 05 '14 10:11

Electric Coffee


3 Answers

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.

like image 67
Mike Seymour Avatar answered Oct 02 '22 13:10

Mike Seymour


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)...);
}
like image 28
Anton Savin Avatar answered Oct 02 '22 13:10

Anton Savin


If in doubt, prefer std::function:

  1. 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 );
    
  2. It's more generic (it actually can take a lambda or a plain C function or a functor)

  3. 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
    
like image 26
Frerich Raabe Avatar answered Oct 02 '22 15:10

Frerich Raabe