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

Viewed 1702

Google is showing me lots of SO posts on passing lambdas as arguments, but they all seem to be C++0x/C++11 based (e.g. Use a lambda as a parameter for a C++ function) and I understand this is an area that has improved in more recent C++ versions?

I want to pass what is effectively a very simple delegate return MyGlobals.GetX() as a method variable - in my case MyGlobals is a global object. i.e. in pseudocode

//stores and/or calls the lambda
myObject.SetXGetter({return MyGlobals.GetX()});

MyObject::SetXGetter(lambda?)
{
 this->mLambda = Lambda;
 cout << mLambda();
}

Older C++ versions made this quite messy having to use STL wrappers and temporary types, so in C++17 and above, how neatly can it be done... what would SetGetter look like and can it be called passing the lambda directly in?

(If there are different alternatives for even newer versions, please share)

3 Answers

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(); });

and I understand this is an area that has improved in more recent C++ versions?

Not in general. It has been expanded to some more complex cases, but the simple cases are pretty much identical to C++11.

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

The options remain the same:

  • You can have a template and pass the lambda as-is.
  • Or you can wrap the lambda in a type erasing wrapper such as std::function in which case you can pass it into a non-template function.
  • Or if the lambda is non-capturing, then it can be converted to a function pointer which can also be passed into a non-template.

Which one is simplest can vary on context and personal opinion. Function wrapper works the same in widest number of different cases, to that's a simple choice. Function pointer doesn't involve dynamic allocation and is simplest to understand at a low level. Template doesn't involve any sort of wrapping or conversion, but it is a template.


this->mLambda = Lambda;

Given that you wish to store the callable as a modifiable member, storing it as a lambda is not an option. You're left with function wrapper and function pointer. You can still use a template for the function, but the lambda must be immediately wrapped or converted anyway, so there isn't much to be gained by using a template.

For C++20 and later:

If you don't intend to store the passed function you can do the following

void use_it(auto func) {
    func();
}

You can use concepts perhaps to constrain it even more so that it does not except anything but at least a callable.

If you want to store the argument you will unfortunately still need to name it like the other answers have suggested, and it must be able to convert to the type you specified:

#include <functional>

class MyObject {
public:
    void SetXGetter(auto func) {
        mLambda = std::move(func);
    }

private:
    std::function<int()> mLambda;
};
Related