97

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?

Alexis Wilke
  • 15,168
  • 8
  • 60
  • 116
lurscher
  • 23,085
  • 26
  • 113
  • 178

5 Answers5

93

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.

bdonlan
  • 205,037
  • 27
  • 244
  • 316
  • 1
    Templates 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) – jalf Jun 23 '11 at 18:17
  • 5
    This 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 in `std::function` without using the heap. Other than that a lambda's size has no real meaning. – deft_code Jun 23 '11 at 18:20
  • 2
    @bdonlan: By the way, why is there `&` in `void f(F & lambda)`? – Nawaz Jun 23 '11 at 18:20
  • @jalf, indeed, it can either help or hurt. hard to tell which without measuring – bdonlan Jun 23 '11 at 18:29
  • @Nawaz, passing `F` as a const reference avoids copying the lambda object when passing it as an argument – bdonlan Jun 23 '11 at 18:30
  • 2
    @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. – Nicol Bolas Jun 24 '11 at 01:57
  • @Nicol, ah, good point. I guess a non-const reference would be best here – bdonlan Jun 24 '11 at 02:42
  • 2
    @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. – zennehoy Sep 11 '13 at 08:42
  • I dare to say that this has changed in C++17 – lurscher Feb 01 '20 at 20:18
48

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

Robert
  • 35,442
  • 34
  • 158
  • 205
Nawaz
  • 327,095
  • 105
  • 629
  • 812
21

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

Puddle
  • 1,963
  • 1
  • 11
  • 29
  • 19
    this 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. – Andriy Tylychko Apr 27 '19 at 23:46
  • 2
    Thank you very much for this! – Mike Lee Jan 08 '21 at 18:24
3

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);
}
Kuba hasn't forgotten Monica
  • 88,505
  • 13
  • 129
  • 275
0

Since C++ 20,

void f(auto& lambda);

actually works (it's an abbreviated function template):

When placeholder types (either auto or Concept 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) { /* ... */}
Alexey Romanov
  • 154,018
  • 31
  • 276
  • 433