Home > Software engineering >  Using auto in lambda function declaration
Using auto in lambda function declaration

Time:09-30

When can auto be used as the type specifier of a variable initialized with a lambda function? I'm try to use auto in the following program:

#include <iostream>
#include <functional>

class A
{
    const std::function <void ()>* m_Lambda = nullptr;

public:
    A(const std::function <void ()>& lambda): m_Lambda (&lambda) {}

    void ExecuteLambda()
    {
        (*m_Lambda)();
    }
};

void main()
{
    int i1 = 1;
    int i2 = 2;
    
    const auto lambda = [&]()
    {
        std::cout << "i1 == " << i1 << std::endl;
        std::cout << "i2 == " << i2 << std::endl;
    };

    A a(lambda);
    a.ExecuteLambda();
}

I'm using Visual Studio Community 2019 and when I start executing a.ExecuteLambda(), the program stops with the following exception:

Unhandled exception at 0x76D9B5B2 in lambda.exe: 
Microsoft C    exception: std :: bad_function_call at memory location 0x00B5F434.

If I change the line const auto lambda = [&]() to const std::function <void ()> lambda = [&](), it works perfectly. Why is it not allowed to use auto? Can something be changed to allow it to be used?

CodePudding user response:

A lambda expression does not result in a std::function. Instead, it creates an unnamed unique class type and that has an overload for operator(). When you pass your lambda to A's constructor, it creates a temporary std::function object, and you store a pointer to that temporary object. When A's constructor ends, that temporary object is destroyed, leaving you with a dangling pointer.

To fix this, just get rid of using pointers. That would look like

#include <iostream>
#include <functional>

class A
{
    std::function <void ()> m_Lambda;
public:
    A(const std::function <void ()> lambda): m_Lambda (lambda) {}

    void ExecuteLambda()
    {
        m_Lambda();
    }
};

void main()
{
    int i1 = 1;
    int i2 = 2;
    
    const auto lambda = [&]()
    {
        std::cout << "i1 == " << i1 << std::endl;
        std::cout << "i2 == " << i2 << std::endl;
    };

    A a(lambda);
    a.ExecuteLambda();
}

CodePudding user response:

You are storing a dangling std::function pointer in your A object.

A lambda expression is not a std::function object, it is a compiler-defined type that is assignable to a std::function object.

When you declare lambda using auto, it gets a unique type. To bind this lambda to the A constructor's parameter, a temporary std::function object is created, which you are storing a pointer to. But then, that temporary gets destroyed when the constructor exits, which is why you get the exception when you try to execute the std::function.

When you change the declaration of lambda to std::function instead, the A constructor's parameter is able to bind to that object as-is, and no temporary is created.

You should be passing and storing std::function objects by value instead of by pointer, eg:

#include <iostream>
#include <functional>

class A
{
    std::function<void()> m_Lambda;

public:
    A(std::function<void()> lambda): m_Lambda(lambda) {}

    void ExecuteLambda()
    {
        m_Lambda();
    }
};

int main()
{
    int i1 = 1;
    int i2 = 2;
    
    const auto lambda = [&]()
    {
        std::cout << "i1 == " << i1 << std::endl;
        std::cout << "i2 == " << i2 << std::endl;
    };

    A a(lambda);
    a.ExecuteLambda();

    return 0;
}

Online Demo

  • Related