Alternative to std::function for passing function as argument (callbacks, etc.)

auto callback(std::future<int>) -> void is the declaration of a entity of type void(std::future<int>) called callback. When listed as an argument, the compiler adjusts this to be a pointer-to-function of type void(*)(std::future<int>).

Your lambda is stateless, and as such can be implicitly converted to a function pointer.

Once you add a non-trivial capture, your code will stop compiling:

[argc](std::future<int> number) { 
   std::cout << argc << '\n';

...

Now, ignoring your question content and looking at the title...

There is a modest cost for a std::function because it is a value-type, not a view-type. As a value-type, it actually copies its argument.

You can get around this by wrapping the calling object in a std::ref, but if you want to state "I won't keep this function object around longer than this call", you can write a function_view type as follows:

template<class Sig>
struct function_view;

template<class R, class...Args>
struct function_view<R(Args...)> {
  void* ptr = nullptr;
  R(*pf)(void*, Args...) = nullptr;

  template<class F>
  using pF = decltype(std::addressof( std::declval<F&>() ));

  template<class F>
  void bind_to( F& f ) {
    ptr = (void*)std::addressof(f);
    pf = [](void* ptr, Args... args)->R{
      return (*(pF<F>)ptr)(std::forward<Args>(args)...);
    };
  }
  // when binding to a function pointer
  // even a not identical one, check for
  // null.  In addition, we can remove a
  // layer of indirection and store the function
  // pointer directly in the `void*`.
  template<class R_in, class...Args_in>
  void bind_to( R_in(*f)(Args_in...) ) {
    using F = decltype(f);
    if (!f) return bind_to(nullptr);
    ptr = (void*)f;
    pf = [](void* ptr, Args... args)->R{
      return (F(ptr))(std::forward<Args>(args)...);
    };
  }
  // binding to nothing:
  void bind_to( std::nullptr_t ) {
    ptr = nullptr;
    pf = nullptr;
  }       
  explicit operator bool()const{return pf;}

  function_view()=default;
  function_view(function_view const&)=default;
  function_view& operator=(function_view const&)=default;

  template<class F,
    std::enable_if_t< !std::is_same<function_view, std::decay_t<F>>{}, int > =0,
    std::enable_if_t< std::is_convertible< std::result_of_t< F&(Args...) >, R >{}, int> = 0
  >
  function_view( F&& f ) {
    bind_to(f); // not forward
  }

  function_view( std::nullptr_t ) {}

  R operator()(Args...args) const {
      return pf(ptr, std::forward<Args>(args)...);
  }
};

live example.

This is also useful in that it is a strictly simpler kind of type erasure than std::function, so it could be educational to go over it.


You are using a raw pointer to function.

Unlike std::function, this will not work with a lambda that captures, or with a result of std::bind, or with a general class type that implements operator().


"Alternative to std::function for passing function as argument"

One alternative would be a function pointer (including member function pointer). But std::function is just so much nicer (IMO).

Tags:

C++

C++11