c++0x: proper way to receive a lambda as parameter by reference
Solution 1
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.
Solution 2
I would use template
as:
template<typename Functor>
void f(Functor functor)
{
cout << functor(10) << endl;
}
int g(int x)
{
return x * x;
}
int main()
{
auto lambda = [] (int x) { cout << x * 50 << endl; return x * 100; };
f(lambda); //pass lambda
f(g); //pass function
}
Output:
500
1000
100
Demo : http://www.ideone.com/EayVq
Solution 3
I know it's been 7 years, but here's a way nobody else mentioned:
void foo(void (*f)(int)){
std::cout<<"foo"<<std::endl;
f(1); // calls lambda which takes an int and returns void
}
int main(){
foo([](int a){std::cout<<"lambda "<<a<<std::endl;});
}
Which outputs:
foo
lambda 1
No need for templates or std::function
Solution 4
Since C++ 20,
void f(auto& lambda);
actually works (it's an abbreviated function template):
When placeholder types (either
auto
orConcept auto
) appear in the parameter list of a function declaration or of a function template declaration, the declaration declares a function template, and one invented template parameter for each placeholder is appended to the template parameter list
and it's equivalent to exactly option 2 in @bdonlan's answer:
template<typename F>
void f(F &lambda) { /* ... */}
Solution 5
void f(auto& lambda);
That's close. What will actually compile is:
#include <cassert>
/*constexpr optional*/ const auto f = [](auto &&lambda)
{
lambda();
lambda();
};
int main()
{
int counter = 0;
f([&]{ ++counter; });
assert(counter == 2);
}
Related videos on Youtube
lurscher
Updated on July 08, 2022Comments
-
lurscher almost 2 years
What is the right way to define a function that receives a
int->int
lambda parameter by reference?void f(std::function< int(int) >& lambda);
or
void f(auto& lambda);
I'm not sure the last form is even legal syntax.
Are there other ways to define a lambda parameter?
-
deft_code about 13 yearsWhy would you need the lambda by reference? Do you mean
const&
?
-
-
josesuero about 13 yearsTemplates can also improve I-cache footprint, by eliminating jumps to general non-local code (in this case, execute your lambda directly, without having to jump through the general-purpose
std::function
wrapper first) -
deft_code about 13 yearsThis quote, "if the lambda function object is small enough it may be represented inline in the
std::function
object" is misleading. Lambdas are always available for inline (the compiler can choose not to of course).std::function
implementations generally uses small object optimization to avoid heap allocations. If a lambda has a small enough capture list it will be stored instd::function
without using the heap. Other than that a lambda's size has no real meaning. -
Nawaz about 13 years@bdonlan: By the way, why is there
&
invoid f(F & lambda)
? -
bdonlan about 13 years@jalf, indeed, it can either help or hurt. hard to tell which without measuring
-
bdonlan about 13 years@Nawaz, passing
F
as a const reference avoids copying the lambda object when passing it as an argument -
Nicol Bolas about 13 years@bdonlan: But the const& assumes that the members of the lambda (the capture-by-values) cannot be changed. Which may not be what the user wants.
-
bdonlan about 13 years@Nicol, ah, good point. I guess a non-const reference would be best here
-
zennehoy almost 11 years@bdonlan It's been a while, but passing as non-const reference does not allow construction of a temporary lambda in the function call. An r-value reference would be best here.
-
Andriy Tylychko about 5 yearsthis is limited only to lambdas that can decay to function pointers (lambdas w/o captures), and also requires to specify exact signature (same as for
std::function
case though) while templated version doesn't have this limitation. -
lurscher over 4 yearsI dare to say that this has changed in C++17
-
Mike Lee over 3 yearsThank you very much for this!
-
sandor about 3 yearsAnd as slightly mentioned, it would probably be even better to constrain F with some concepts based on the needs of
f()