How to check if the c++ lambda is captured correctly?

Viewed 151

Why does this code run incorrectly ? See the function "f1". It assigns the "t" to std::function. I list 3 ways to do that. But the first one is error.It will output incorrect value. If the code is complicated, the program may even crash. I have already knew the reason is that lambda "t" is a local variable so that we must capture it by value. But such code can be compiled without errors or warnings, is there a way(any compile option?) to find such error when compiling?

#include <iostream>
#include<functional>

struct aa
{
    int a = 2000;
    void f()
    {
        ++a;
        printf("data = %d\n", a);
    }
};

std::function<void()> gf;
template<typename T>
void f1(T t)
{

    1 //gf = [&] {t(); };
    2 //gf = [=] {t(); };
    3 //gf=t;
}

int main(int argc, char** argv)
{
    aa b2;
    f1([&] {b2.f(); });
    gf();
    return 0;
}

online test: https://godbolt.org/z/5bqGeof58

update: Latest clang can detect very simple case, but don't work for code above. example:

auto f(int a)
{
    return [&]{return a;};  //warning: address of stack memory associated with parameter 'a' returned [-Wreturn-stack-address]
}
int main(int argc, char** argv)
{
    printf("%d\n",f(300)());
    return 0;
}
1 Answers

is there a way(any compile option?) to find such error when compiling?

AFAIK, no common compiler will do this for you since that's far beyond the core job of a compiler. It's not the job of a compiler to apply program data "meta" lifetime analysis (even though, depending on the optimization level, optimizers can do that in a certain way potentially but with a lot of constraints, not appropriate to replace runtime analyzer tools).

As a best practice, lines like this

gf = [&] {t(); };

should always trigger extra attention for you to handle things correctly, at least as soon as your lambda (call) exceeds its scope life time. If you are not sure about the life time of concrete objects, you could first take use of explicit tracking via shared and weak pointers for the reference case to lvalues at least and change the scheme to raw reference usage as soon as you are convinced, that the references maintain valid (from a design point of view, not capturing by reference always only in order to avoid subtile bugs could be questioned too, since it exposes a general design flaw or the uncertainty of the author...). Of course, that still requires a runtime checking via appropriate tests.

Related