Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Lambda function variables in C++11

There are two ways to use lambda function variable:

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

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

And:

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

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

I would like to know what are the differences, because I do not know how the lambda function data is stored.

I would like to know the best way in terms of peformance, memory usage and the best way to pass a lambda function as argument or return lambda function.
I would prefer to use pointers if the size of the lambda function object is bigger than 4 or to avoid errors (if some kind of copy constructor is executed when I do an attribution or if some kind of destructor is executed when I do not want).

How should I declare lambda function variables?

EDIT

I want to avoid copies and moves, I want to continue to use the same function again.

How should I change this example?

int call1(std::function<int(int, int)> f){
    return f(1, 2);
}
int call2(std::function<int(int, int)> f){
    return f(4, 3);
}
std::function<int(int, int)>& recv(int s){
    return [=](int a, int b) -> int{return a*b + s;};
}

int main(){
    std::function<int(int, int)> f1, f2;

    f1 = [=](int a, int b) -> int{return a + b;};
    f2 = recv(10);

    call1(f1);
    call2(f1);
    call1(f2);
    call2(f2);
}

I cannot return reference in the function recv:

 warning: returning reference to temporary

Is this a good solution?

int call1(std::function<int(int, int)>* f){
    return (*f)(1, 2);
}
int call2(std::function<int(int, int)>* f){
    return (*f)(4, 3);
}

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

int main(){
    std::function<int(int, int)> f1 = [=](int a, int b) -> int{return a + b;};
    std::function<int(int, int)> *f2 = recv(10);

    call1(&f1);
    call2(&f1);
    call1(f2);
    call2(f2);

    delete f2;
}

EDIT (Conclusion)

A lambda function object is like any object that is instance of a class. The rules for allocation, arguments and attribution are the same.

like image 797
Squall Avatar asked Apr 14 '12 16:04

Squall


2 Answers

It doesn't make sense to create pointer of std::function. It just doesn't benefit you in any way. Instead of benefits, it puts more burden on you, as you've to delete the memory.

So the first version (i.e the non-pointer version) is all that you should go for.

In general, like a rule of thumb, avoid new as much as possible.

Also, you don't need std::function everytime you use lambda. Many times, you could just use auto as:

auto add = [](int a, int b) { return a + b;};

std::cout << add(100,100) << std::endl;
std::cout << add(120,140) << std::endl;
like image 161
Nawaz Avatar answered Oct 14 '22 04:10

Nawaz


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.

like image 37
Nicol Bolas Avatar answered Oct 14 '22 05:10

Nicol Bolas