Lambda function variables in C++11

24,232

Solution 1

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.

Solution 2

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;
Share:
24,232
Squall
Author by

Squall

Updated on February 06, 2020

Comments

  • Squall
    Squall about 4 years

    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.

    • Nicol Bolas
      Nicol Bolas about 12 years
      "I would prefer to use pointers if the size of the lambda function object is bigger than 4" Why? Do you think the stack will implode if an object larger than 4 bytes is on it? Standard C++ practice is to put objects on the stack by default, unless you have a good reason not to. And being larger than 4 bytes isn't one.
  • Seth Carnegie
    Seth Carnegie about 12 years
    Also, passing things on the stack is usually much faster than allocating things on the free store and passing pointers to them and having to apply indirection.
  • Admin
    Admin about 12 years
    @Seth Carnegie And you won't have any headaches because of manual memory management and exception-unsafety.
  • Nicol Bolas
    Nicol Bolas about 12 years
    "Many times" is a bit of a stretch. He obviously wants to store it somewhere else, hence is accessor functions.
  • Squall
    Squall about 12 years
    I cannot return reference allocated on stack. I cannot use move, because I will need to use the function again, I think that traditional pointer would help.
  • Squall
    Squall about 12 years
    If I need to pass a lambda function as argument or return a lambda function, should I use pointers?
  • Nicol Bolas
    Nicol Bolas about 12 years
    @Squall: You get function would presumably be retrieving it from some storage, like the member of an object or something. And if not, then just return a value and let NVRO or move-construction take care of it, since you're talking about a stack parameter. And no, the "traditional pointer" is not helpful (and isn't "traditional" in C++ anymore). All it will do is create more bugs. If you still need to use the object, then use it beforehand or just copy it. Otherwise, you have to sort out who owns it, which without smart pointers is very error prone. Not to mention exception safety.
  • Nicol Bolas
    Nicol Bolas about 12 years
    @Squall: It seems to me that you've already decided on what you want to do, but you know it's not kosher, and are trying to find validation for your plan. If you are willing to ignore everyone's advice and just heap-allocate them into naked pointers and do the "traditional" thing (including the traditional memory leaks, double-deletion, and lack of exception safety), then do it. If you want actual advice, then copy the object and/or move it, as you need to. Or at the very least, use a smart pointer.
  • Nawaz
    Nawaz about 12 years
    @Squall: NO. Use the first version.