How to Receive a Lambda as Parameter by Reference

Proper way to receive a lambda as parameter by reference

You cannot have an auto parameter. You basically have two options:

Option #1: Use std::function as you have shown.

Option #2: Use a template parameter:

template<typename F>
void f(F && lambda) { /* ... */}

Option #2 may, in some cases, be more efficient, as it can avoid a potential heap allocation for the embedded lambda function object, but is only possible if f can be placed in a header as a template function. It may also increase compile times and I-cache footprint, as can any template. Note that it may have no effect as well, as if the lambda function object is small enough it may be represented inline in the std::function object.

Don't forget to use std::forward<F&&>(lambda) when referring to lambda, as it is an r-value reference.

Use a lambda as a parameter for a C++ function

You have 2 ways: make your function template:

template <typename F>
void myFunction(F&& lambda)
{
//some things
}

or erase type (with std::function for example):

void
myFunction(const std::function<void()/*type of your lamdba::operator()*/>& f)
{
//some things
}

passing lambda as argument - by reference or value?

As a possible drawback, note that passing by copy could not work if the lambda isn't copyable. If you can get away with it, passing by copy is just fine.

As an example:

#include<memory>
#include<utility>

template<typename F>
void g(F &&f) {
std::forward<F>(f)();
}

template<typename F>
void h(F f) {
f();
}

int main() {
auto lambda = [foo=std::make_unique<int>()](){};

g(lambda);
//h(lambda);
}

In the snippet above, lambda isn't copyable because of foo. Its copy constructor is deleted as a consequence of the fact that the copy constructor of a std::unique_ptr is deleted.

On the other side, F &&f accepts both lvalue and rvalue references being it a forwarding reference, as well as const references.

In other terms, if you want to reuse the same lambda as an argument more than once, you cannot if your functions get your object by copy and you must move it for it's not copyable (well, actually you can, it's a matter of wrapping it in a lambda that captures the outer one by reference).

Simplest way to pass a lambda as a method parameter in C++17

A function that just wants to invoke a passed lambda or other functor during its own execution can be a function template, deducing the type of the functor:

template <typename F>
void use_it(F&& func) {
do_something_with(func());
}

Here you want to store the functor to be invoked later, so we'll need a common type that can wrap around various sorts of functors. This is what std::function is for. Assuming a return type of int:

#include <functional>

class MyObject {
// ...
private:
std::function<int()> mLambda;
};
void MyObject::SetXGetter(std::function<int()> func) {
mLambda = std::move(func);
}

Your call is missing the initial [captures] which is required for a lambda expression, even if nothing is captured:

myObject.SetXGetter([]{ return MyGlobals.GetX(); });

How to pass a Lambda Function as a parameter

Use std::function like for example

#include <iostream>
#include <functional>

double receives( const std::function<double( double )> &f )
{
return f( 5 );
}

int main()
{
int y = 5;

auto fun = [y](double x)
{
return x + y;
};

std::cout << receives( fun ) << '\n';

return 0;
}

How to pass reference to function with lambda

You are passing a lambda to a std::function &. A lambda is not a std::function so that doesn't work directly. However, a lambda is convertible to a std::function, so a temporary std::function has to be build that holds the lambda.

Since you are taking a std::function & C++ assumes you want to modify that value, otherwise there would be no point in taking it by non-const reference. Modifying a temporary has no effect, so this doesn't make sense, so C++ doesn't allow it.

One way to fix the issue is to take a const std::function &. That way there is no modifying of a temporary.

Another way is to pass an actual std::function that you create in main.

Another way is to create a template that just forwards it's argument to the std::function:

class MyClass
{
public:
template <class T>
MyClass(T &&t)
: fun(std::forward<T>(t)){}
private:
std::function<int(int, int)> fun;
};

That way MyClass takes anything and uses it to construct fun. Note that fun is not a reference anymore. The std::function has to live somewhere and since it is part of MyClass it makes sense to tie the lifetime of the std::function to MyClass.

If you really want fun to be a reference you need to find a place for it and make sure it stays there until after MyClass a; gets destroyed. I do not recommend this route since it gets increasingly difficult to make sure the lifetimes are correct.

You can also store the lambda directly, but that is a bit tricky and depending on what you actually want to do with MyClass it may not be viable.

Use lambda to modify references identified by a packed parameter?

You can do some sneaky tricks to get the signature of the lambda and use it to get the components.

template <typename Class, typename... Params>
void mod_comp_helper(Entity ent, Class *obj, void (Class::*fun)(Params...) const) {
(obj->*fun)(get_comp<std::decay_t<Params>>(ent)...);
}

// optional overload for mutable lambdas
template <typename Class, typename... Params>
void mod_comp_helper(Entity ent, Class *obj, void (Class::*fun)(Params...)) {
(obj->*fun)(get_comp<std::decay_t<Params>>(ent)...);
}

template <typename Functor>
void mod_comp(Entity ent, Functor &&fun) {
mod_comp_helper(ent, &fun, &std::decay_t<Functor>::operator());
}

// optional overload for function pointers
template <typename... Params>
void mod_comp(Entity ent, void(*fun)(Params...)) {
fun(get_comp<std::decay_t<Params>(ent)>...);
}

int main() {
mod_comp(ent, [](Velocity &vel, Position &pos) {
// modify components
});

// you can use std::function if you want
// although you probably don't need to
std::function<void(Velocity &, Position &)> fun = [](Velocity &vel, Position &pos) {
// modify components
};

mod_comp(ent, fun);

// this calls the function pointer overload
mod_comp(ent, +[](Velocity &vel, Position &pos) {
// modify components
});
}

A lambda expression is actually just syntax sugar for constructing a functor (an object with a call operator).

struct __anonymous_compiler_generated_class__ {
void operator()(int i) const {
// ...
}
};

int main() {
auto lambda = [](int i) {
// ...
};
// above is sugar for this:
auto functor = __anonymous_compiler_generated_class__{};
}

A lambda expression constructs a closure object. This object has an operator(). We can take the address of the call operator and deduce its signature. Then we just std::decay_t the parameter types to remove references and const-ness.

Another neat trick with lambdas is converting them to function pointers (which I showed in the first example). Non-capturing lambdas can be implicitly converted to function pointers. You can use a unary + or a static_cast to force this conversion. Here are a few more examples of that:

int main() {
auto lambda = [](int i) {};
void (*fnptr0)(int) = lambda;
auto fnptr1 = +lambda;
auto fnptr2 = static_cast<void(*)(int)>(lambda);

int capture;
auto capturing_lambda = [capture](int i) {};
// compiler says no
// auto fnptr3 = +capturing_lambda;
}

If you don't need capturing lambdas and can tolerate the unary + then you could just use the function pointer overload of mod_comp but for this, you probably want capturing lambdas.

Passing by constant reference in the lambda capture list

You can create and capture const references explicitly:

int x = 42;
const int& rx = x;
auto l = [&rx]() {
x = 5; // error: 'x' is not captured
rx = 5; // error: assignment of read-only reference 'rx'
};

Should I pass a lambda by const reference.

If you pass by value you will copy the closure object (assuming you don't define the lambda inline, in which case it will be moved). This might be undesirable if the state is expensive to copy, and will fail to compile if the state is not copyable.

template <class Function>
void higherOrderFunction(Function f);

std::unique_ptr<int> p;
auto l = [p = std::move(p)] {}; // C++14 lambda with init capture
higherOrderFunction(l); // doesn't compile because l is non-copyable
// due to unique_ptr member
higherOrderFunction([p = std::move(p)] {}); // this still works, the closure object is moved

If you pass by const reference, then you cannot pass a mutable lambda that modifies its data members as the argument to higherOrderFunction() because a mutable lambda has a non-const operator(), and you cannot invoke that on a const object.

template <class Function>
void higherOrderFunction(Function const& f);

int i = 0;
higherOrderFunction([=]() mutable { i = 0; }); // will not compile

The best option is to use a forwarding reference. Then higherOrderFunction can accept either lvalues or rvalues that the caller passes.

template <class Function>
void higherOrderFunction(Function&& f) {
std::forward<Function>(f)();
}

This allows the simple cases as well as the ones mentioned above to compile. For a discussion of why std::forward should be used, see this answer.

Live demo



Related Topics



Leave a reply



Submit