How to Store a Lambda Expression as a Field of a Class in C++11

How can I store a lambda expression as a field of a class in C++11?

If you want a class member to be a lambda expression, consider using the std::function<> wrapper type (from the <functional> header), which can hold any callable function. For example:

std::function<int()> myFunction = [] { return 0; }
myFunction(); // Returns 0;

This way, you don't need to know the type of the lambda expression. You can just store a std::function<> of the appropriate function type, and the template system will handle all the types for you. More generally, any callable entity of the appropriate signature can be assigned to a std::function<>, even if the the actual type of that functor is anonymous (in the case of lambdas) or really complicated.

The type inside of the std::function template should be the function type corresponding to the function you'd like to store. So, for example, to store a function that takes in two ints and returns void, you'd make a std::function<void (int, int)>. For a function that takes no parameters and returns an int, you'd use std::function<int()>. In your case, since you want a function that takes no parameters and returns void, you'd want something like this:

class MyClass { 
public:
std::function<void()> function;
MyClass(std::function<void()> f) : function(f) {
// Handled in initializer list
}
};

int main() {
MyClass([] {
printf("hi")
}) mc; // Should be just fine.
}

Hope this helps!

C++11 lambda as member variable?

Templates make it possible without type erasure, but that's it:

template<typename T>
struct foo {
T t;
};

template<typename T>
foo<typename std::decay<T>::type>
make_foo(T&& t)
{
return { std::forward<T>(t) };
}

// ...
auto f = make_foo([] { return 42; });

Repeating the arguments that everyone has already exposed: []{} is not a type, so you can't use it as e.g. a template parameter like you're trying. Using decltype is also iffy because every instance of a lambda expression is a notation for a separate closure object with a unique type. (e.g. the type of f above is not foo<decltype([] { return 42; })>.)

Lambda expressions as class template parameters

As of C++20, this answer is now outdated. C++20 introduces stateless lambdas in unevaluated contexts1:

This restriction was originally designed to prevent lambdas from appearing in signatures, which would have opened a can of worm for mangling because lambdas are required to have unique types. However, the restriction is much stronger than it needs to be, and it is indeed possible to achieve the same effect without it

Some restrictions are still in place (e.g. lambdas still can't appear on function signatures), but the described usecase is now completely valid and the declaration of a variable is no longer necessary.



I'm asking if you can do something like:

Foo<decltype([]()->void { })> foo;

No you can't, because lambda expressions shall not appear in an unevaluated context (such as decltype and sizeof, amongst others).
C++0x FDIS, 5.1.2 [expr.prim.lambda] p2

The evaluation of a lambda-expression results in a prvalue temporary (12.2). This temporary is called the
closure object. A lambda-expression shall not appear in an unevaluated operand (Clause 5). [ Note: A
closure object behaves like a function object (20.8).—end note ]
(emphasis mine)

You would need to first create a specific lambda and then use decltype on that:

auto my_comp = [](const std::string& left, const std::string& right) -> bool {
// whatever
}

typedef std::unordered_map<
std::string,
std::string,
std::hash<std::string>,
decltype(my_comp)
> map_type;

That is because each lambda-derived closure object could have a completely different type, they're like anonymous functions after all.

Lambda function variables in C++11

You've made it clear that you want to use pointers. So... do that.

But at least use a smart pointer. Proper use of std::shared_ptr would prevent a number of problems. Of course, you have to make sure to avoid circular references, but for lambdas, that seems unlikely.

Granted, it's still a terrible idea. But it's only terrible in the sense of being a completely pointless premature optimization done to make yourself feel better about passing things around, rather than having any actual measurable benefit in your code's actual running time. At least with a shared_ptr, you're less likely to accidentally delete the memory or run into exception safety issues.


I'm going to ignore the bad C++ practice of heap-allocating something you could just as easily stack allocate, as well as the pain of tracking this memory without the use of a smart pointer. This will assume that you know what you're doing and will instead focus on the "performance" and "memory usage" issues you asked about.

Do not take this as an endorsement of what you're wanting to do.

std::function will generally be implemented by doing an internal heap allocation of some object; that's necessary due to the type-erasure. So it will be at least one pointer in size; possibly more. The size of the heap allocation will likely be a vtable pointer + the size of your lambda class. And the size of that is governed by how much stuff you capture and whether it is by value or reference.

Like most C++ standard library objects, std::function is copyable. Performing a copy will actually copy it, not do a pretend copy where you now have two objects with the same pointer. So each one will have an independent copy of the internal heap object. That is, copying it will mean doing another heap allocation. This will also copy the lambda itself, which means copying everything that's captured within it.

However, like most C++ standard library objects, std::function is movable. This does not do any memory allocation whatsoever.

Therefore, if you want to do this, it's pretty cheap:

std::function<int(int, int)> x1 = [=](int a, int b) -> int{return a + b;};

//usage
void set(std::function<int(int, int)> x);
const std::function<int(int, int)> &get();

set(std::move(x1)); //x1 is now *empty*; you can't use it anymore.

Your set function can move it into its own internal storage as needed. Note that get now returns a const&; this is contingent on the storage for these functions not going anywhere.

How cheap will move be? It will probably be the equivalent of copying just the fields of std::function, as well as blanking or otherwise neutralizing the original one. Unless you're in performance-critical code, this will not be anything you should ever be concerned about.

saving a lambda expression as parameter/variable inside a class to another class

You define

Learning::addAction(Action&) 

with an action reference as argument. This means that you have to ensure the lifetime of the referenced object, and not let it get destroyed by going out of scope. But then you call it on an rvalue

// Action(...) immediately goes out of scope
robot.addAction(Action("Up", [](int y) ->int{return y++; }));

Therefore the references you put in your vectors are all invalid : they point toward long gone objects. You need to take ownership of them if you want it to work. This means :

class Learning
{
public:
void addAction(Action&&);
private:
std::vector<Action> actions;
};

// rvalue reference, so the vector can take ownership of the expiring object
void Learning::addAction(Action&& act)
{
actions.push_back(std::move(act));
}

lambda expression to assign a member function pointer

From the comments:

This is part of an ECS implemention. and I am simply not willing to create a new class for etch system i want to give the user the option to declare the system in the scene constructor or inheriate from the system class.

You want different behavior from the same class without any indirection? You'll have to give up one.

But you don't have to write a class for each system either. You can make the class a template, so the compiler can generate a class for each systems:

template<typename T>
struct A : private T {
A(T function) noexcept : T{std::move(function)} {}

void SomeFunction() {
(*this)(this);
}

int x = 0;
};

It can then be used like that:

auto lambda = [](auto a){ printf("Hello from lambda %d",a->x); };
auto my_a = A{lambda}; // Generate a new A type from lambda

my_a.SomeFunction(); // calls the lambda!

How are C++11 lambdas represented and passed?

Disclaimer: my answer is somewhat simplified compared to the reality (I put some details aside) but the big picture is here. Also, the Standard does not fully specify how lambdas or std::function must be implemented internally (the implementation has some freedom) so, like any discussion on implementation details, your compiler may or may not do it exactly this way.

But again, this is a subject quite similar to VTables: the Standard doesn't mandate much but any sensible compiler is still quite likely to do it this way, so I believe it is worth digging into it a little. :)


Lambdas

The most straightforward way to implement a lambda is kind of an unnamed struct:

auto lambda = [](Args...) -> Return { /*...*/ };

// roughly equivalent to:
struct {
Return operator ()(Args...) { /*...*/ }
}
lambda; // instance of the unnamed struct

Just like any other class, when you pass its instances around you never have to copy the code, just the actual data (here, none at all).


Objects captured by value are copied into the struct:

Value v;
auto lambda = [=](Args...) -> Return { /*... use v, captured by value...*/ };

// roughly equivalent to:
struct Temporary { // note: we can't make it an unnamed struct any more since we need
// a constructor, but that's just a syntax quirk

const Value v; // note: capture by value is const by default unless the lambda is mutable
Temporary(Value v_) : v(v_) {}
Return operator ()(Args...) { /*... use v, captured by value...*/ }
}
lambda(v); // instance of the struct

Again, passing it around only means that you pass the data (v) not the code itself.


Likewise, objects captured by reference are referenced into the struct:

Value v;
auto lambda = [&](Args...) -> Return { /*... use v, captured by reference...*/ };

// roughly equivalent to:
struct Temporary {
Value& v; // note: capture by reference is non-const
Temporary(Value& v_) : v(v_) {}
Return operator ()(Args...) { /*... use v, captured by reference...*/ }
}
lambda(v); // instance of the struct

That's pretty much all when it comes to lambdas themselves (except the few implementation details I ommitted, but which are not relevant to understanding how it works).


std::function

std::function is a generic wrapper around any kind of functor (lambdas, standalone/static/member functions, functor classes like the ones I showed, ...).

The internals of std::function are pretty complicated because they must support all those cases. Depending on the exact type of functor this requires at least the following data (give or take implementation details):

  • A pointer to a standalone/static function.

Or,

  • A pointer to a copy[see note below] of the functor (dynamically allocated to allow any type of functor, as you rightly noted it).
  • A pointer to the member function to be called.
  • A pointer to an allocator that is able to both copy the functor and itself (since any type of functor can be used, the pointer-to-functor should be void* and thus there has to be such a mechanism -- probably using polymorphism aka. base class + virtual methods, the derived class being generated locally in the template<class Functor> function(Functor) constructors).

Since it doesn't know beforehand which kind of functor it will have to store (and this is made obvious by the fact that std::function can be reassigned) then it has to cope with all possible cases and make the decision at runtime.

Note: I don't know where the Standard mandates it but this is definitely a new copy, the underlying functor is not shared:

int v = 0;
std::function<void()> f = [=]() mutable { std::cout << v++ << std::endl; };
std::function<void()> g = f;

f(); // 0
f(); // 1
g(); // 0
g(); // 1

So, when you pass a std::function around it involves at least those four pointers (and indeed on GCC 4.7 64 bits sizeof(std::function<void()> is 32 which is four 64 bits pointers) and optionally a dynamically allocated copy of the functor (which, as I already said, only contains the captured objects, you don't copy the code).


Answer to the question

what is the cost of passing a lambda to a function like this?[context of the question: by value]

Well, as you can see it depends mainly on your functor (either a hand-made struct functor or a lambda) and the variables it contains. The overhead compared to directly passing a struct functor by value is quite negligible, but it is of course much higher than passing a struct functor by reference.

Should I have to mark each function object passed with const& so that a copy is not made?

I'm afraid this is very hard to answer in a generic way. Sometimes you'll want to pass by const reference, sometimes by value, sometimes by rvalue reference so that you can move it. It really depends on the semantics of your code.

The rules concerning which one you should choose are a totally different topic IMO, just remember that they are the same as for any other object.

Anyway, you now have all the keys to make an informed decision (again, depending on your code and its semantics).



Related Topics



Leave a reply



Submit