c++0x: proper way to receive a lambda as parameter by reference

113,323

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 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) { /* ... */}

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);
}
Share:
113,323

Related videos on Youtube

lurscher
Author by

lurscher

Updated on July 08, 2022

Comments

  • lurscher
    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
      deft_code about 13 years
      Why would you need the lambda by reference? Do you mean const&?
  • josesuero
    josesuero about 13 years
    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)
  • deft_code
    deft_code about 13 years
    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.
  • Nawaz
    Nawaz about 13 years
    @bdonlan: By the way, why is there & in void f(F & lambda)?
  • bdonlan
    bdonlan about 13 years
    @jalf, indeed, it can either help or hurt. hard to tell which without measuring
  • bdonlan
    bdonlan about 13 years
    @Nawaz, passing F as a const reference avoids copying the lambda object when passing it as an argument
  • Nicol Bolas
    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
    bdonlan about 13 years
    @Nicol, ah, good point. I guess a non-const reference would be best here
  • zennehoy
    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
    Andriy Tylychko about 5 years
    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.
  • lurscher
    lurscher over 4 years
    I dare to say that this has changed in C++17
  • Mike Lee
    Mike Lee over 3 years
    Thank you very much for this!
  • sandor
    sandor about 3 years
    And as slightly mentioned, it would probably be even better to constrain F with some concepts based on the needs of f()