44

The code I'm looking for is like following.

bool Func1(int Arg1, C++11LambdaFunc Arg2){
    if(Arg1 > 0){
        return Arg2(Arg1);
    }
}

Later I'll be using this code.

Func1(12, [](int D) -> bool { ... } );
Tomáš Zato
  • 50,171
  • 52
  • 268
  • 778
Mohsen Sarkar
  • 5,910
  • 7
  • 47
  • 86

3 Answers3

63

Basic version, for use in a header file:

template<typename Lambda>
bool Func1(int Arg1, Lambda Arg2){ // or Lambda&&, which is usually better
  if(Arg1 > 0){
    return Arg2(Arg1);
  } else {
    return false; // remember, all control paths must return a value
  }
}

More complex version, if you want to split your interface from your implementation (it has run time costs):

bool Func1(int Arg1, std::function<bool(int)> Arg2){
  if(Arg1 > 0){
    return Arg2(Arg1);
  } else {
    return false; // remember, all control paths must return a value
  }
}

std::function uses type erasure to create a custom-created wrapper around your lambda, and then exposes a non-virtual interface that uses the pImpl pattern to forward it to the custom-created wrapper.1

Or, in less technical terms, std::function<bool(int)> is a class that can wrap nearly anything that you can call like a function, passing one parameter that is compatible with passing an int, and it returns something that is compatible with returning a bool.

A call through a std::function has a run time cost roughly equal to a virtual function call (caused by the above type erasure), and when you create it it has to copy the state of the function object (aka functor) passed in (which can be cheap -- stateless lambdas, or lambdas capturing arguments by reference -- or expensive in some other cases) and store it (typically on the free store or heap, which has a cost), while the pure-template versions can be "inlined" at the point of call (ie, can not only cost less than a function call, the compiler can even optimize over the function call and return boundaries!)

If you want to split interface/implementation without all of the runtime costs of std::function, you can roll your own function_ref (in , because that cuts down on some boilerplate):

template<class Sig>
struct function_ref;

template<class R, class...Args>
struct function_ref<R(Args...)> {
  R operator()(Args...args) const {
    return pf(state, std::forward<Args>(args)...);
  }
  function_ref()=default;
  function_ref(function_ref const&)=default;
  function_ref& operator=(function_ref const&)=default;
  explicit operator bool()const{ return pf!=nullptr; }

  // this overload reduces indirection by 1 step
  // and allows function_ref<Sig> to resolve overloads
  // on an overload set sometimes.
  function_ref( R(*f)(Args...) ):
    pf([](State const& state, Args&&...args)->R{
      return reinterpret_cast<R(*)(Args...)>(state.pfunstate)(std::forward<Args>(args)...);
    })
  {
    state.pfunstate = reinterpret_cast<void(*)()>(f);
  }

  // this grabs anything callable (that isn't this own type)
  // and stores a pointer to it to call later.
  template<class F>
  requires (
    std::is_convertible_v<
      std::invoke_result_t< std::remove_reference_t<F>, Args... >, R
    >
    && !std::is_same_v< std::decay_t<F>, function_ref >
  )
  function_ref( F&& f ):
    pf([](State const& state, Args&&...args)->R{
      return (*(std::remove_reference_t<F>*)state.pstate)(std::forward<Args>(args)...);
    })
  {
    state.pstate = std::addressof(f);
  }
private:
  union State {
    void* pstate = nullptr;
    void(*pfunstate)();
  };
  State state;
  R(*pf)(State const&, Args&&...) = nullptr;
};
// a deduction guide permitting function_ref{foo} to work
// if foo is a non-overloaded function name.
template<class R, class...Args>
function_ref( R(*)(Args...) )->function_ref<R(Args...)>;

Live example.

This removes the need to ever do any allocation from std::function by removing ownership semantics from it and just type-erasing calling.

A fancy version of the first example that also handles some corner cases a tad better: (also must be implemented within a header file, or in the same translation unit as it is used)

template<typename Lambda>
bool Func1(int Arg1, Lambda&& Arg2){
  if(Arg1 > 0){
    return std::forward<Lambda>(Arg2)(Arg1);
  } else {
    return false; // remember, all control paths must return a value
  }
}

which uses a technique known as "perfect forwarding". For some functors, this generates slightly different behavior than #1 (and usually more correct behavior).

Most of the improvement comes form the use of && in the argument list: this means that a reference to the functor is passed in (instead of a copy), saving some costs, and allows both a const or non-const functor to be passed in.

The std::forward<Lambda>(...) change would only cause a change in behavior if someone used a relatively new C++ feature that allows methods (including operator()) to override on the rvalue/lvalue status of the this pointer. In theory, this could be useful, but the number of functors I've seen that actually override based on the rvalue status of this is 0. When I'm writing serious library code (tm) I go to this bother, but rarely otherwise.

There is one more possible thing to consider. Suppose you want to take either a function that returns bool, or a function that returns void, and if the function returns void you want to treat it as if it returned true. As an example, you are taking a function that is being called when iterating over some collection, and you want to optionally support early halting. The function returns false when it wants to stop prematurely, and true or void otherwise.

Or, in a more general case, if you have multiple overrides of a function, one of which takes a function and others take some other type at the same location.

This is possible, which is as far as I'm going to get into here (either with a smart adapter, or via SFINAE techniques). However, you are probably better off just creating two different named functions, because the techniques required are way too heavy weight.


1 Technically std::function could use magic fairy dust to do what it does, as its behavior is described by the standard, and not its implementation. I'm describing a simple implementation that approximates the behavior of the std::function implementation I have interacted with.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
  • I believe you can also pass function like "const std::function & func". Is it better/worse than move && thing? I always thought that "const &" means just "pass this object", but && means moving the object state to a new instance (which may call move copy ctor?) – Aleksei Petrenko Jun 14 '14 at 00:32
  • 1
    @AlexPetrenko `&&` is an rvalue reference typically, but it is also used in the technique called ["perfect forwarding"](http://stackoverflow.com/questions/3582001/advantages-of-using-forward)/["universal references"](http://stackoverflow.com/questions/14302849/syntax-for-universal-references), where `T&&` can be an rvalue reference **or** an lvalue reference (in a type deduction context -- technically anywhere, but rarely used outside of type deduction that way). – Yakk - Adam Nevraumont Jun 14 '14 at 01:07
25

First solution:

You can make your Func1() function a function template:

template<typename T>
bool Func1(int Arg1, T&& Arg2){
    if(Arg1 > 0){
        return Arg2(Arg1);
    }

    return false; // <== DO NOT FORGET A return STATEMENT IN A VALUE-RETURNING
                  //     FUNCTION, OR YOU WILL GET UNDEFINED BEHAVIOR IF FLOWING
                  //     OFF THE END OF THE FUNCTION WITHOUT RETURNING ANYTHING
}

You could then invoke it as you desire:

int main()
{
    Func1(12, [](int D) -> bool { return D < 0; } );
}

Second solution:

If you do not want to use templates, an alternative (that would bring some run-time overhead) is to use std::function:

#include <functional>

bool Func1(int Arg1, std::function<bool(int)> Arg2){
    if(Arg1 > 0){
        return Arg2(Arg1);
    }

    return false;
}

Once again, this would allow you to call Func1() the way you desire:

int main()
{
    Func1(12, [](int D) -> bool { return D < 0; } );
}
Andy Prowl
  • 124,023
  • 23
  • 387
  • 451
  • +1: 4 seconds too slow on my answer. Technically you should use `std::forward(Arg2)(Arg1)`, but that is rarely worth the bother. – Yakk - Adam Nevraumont Apr 19 '13 at 18:36
  • 2
    @Yakk: Well, I guess that would make no difference when a lambda or a function is being called, right? You're not forwarding anything after all. It *could* make some difference with a functor, in case the functor is using references to `this` (as a function qualifier, I mean `operator () (...) &&`) - but... heh, not worth bothering IMO :) – Andy Prowl Apr 19 '13 at 18:39
  • 1
    In theory, either `[myvec](int x) { myvec.push_back(x); return myvec; }` or a manual equivalent could realize that it is being evaluated in an `rvalue` context and return-by-move. But yes, it is pretty darn obscure. – Yakk - Adam Nevraumont Apr 19 '13 at 18:43
  • @AndyProwl Why not `forward`? Even though it may not matter for this simple case, its better to use forward – balki Apr 19 '13 at 18:51
  • 1
    @balki: Concretely, it does not make any difference in this case - and if it doesn't make any difference, I prefer the more readable version. – Andy Prowl Apr 19 '13 at 18:56
13

For those whose tastes are more traditional, note that non-capturing lambdas can convert to function pointers. So you can write your function above as:

bool Func1(int Arg1, bool (*Arg2)(int)) { ... }

And it will work correctly for both traditional functions and lambdas.

Andy Ross
  • 11,699
  • 1
  • 34
  • 31
  • using a template allows you to capture function pointers, references to functions, function-objects, lambdas with or without captures), std::functions, results of std::bind, etc. – bstamour Apr 19 '13 at 19:05
  • 1
    Using a template also won't work if you want to pass a pre-existing function or need to interoperate with C code. Not really interested in the C++ purity side of the argument; this was simply a bit of the language I thought was being missed by the existing answers. – Andy Ross Apr 19 '13 at 20:06
  • I get the C interface argument, but templates will totally bind to a pre-existing function. If your object can be called like a function, then a template will work. – bstamour Apr 19 '13 at 23:37