Reputation: 43
Consider the following minimal code:
#include <functional>
#include <iostream>
#include <string>
#include <vector>
class A
{
public:
A() :
s("dummy"),
f([&, this]{ return s == "dummy"; }),
{}
std::string s;
std::function<bool ()> f;
};
int main(int argc, char *argv[])
{
A a;
a.f(); // seems fine
std::vector<A> as({});
as[0].f(); // segmentation fault
}
Class A
has a lambda member that captures this
pointer. When running the code above, lambda works fine when invoked from standalone A
instance, but I get segmentation fault when called from an instance stored in the vector.
Why does it happen?
Upvotes: 3
Views: 804
Reputation: 109119
There are a couple of different problems with your example. This line
std::vector<A> as({});
constructs an empty vector
. When you index into the first element on the next line, you have undefined behavior, and your program crashes in this case.
If you change that to
std::vector<A> as;
as.emplace_back();
a[0].f();
the code will work as you expect, but that doesn't mean there are no more problems. If you add a few more elements to your vector
, it'll reallocate storage to accommodate the new elements, and the previously stored lambdas will get move constructed. But when that happens, they will still retain their old this
pointer values, which point to objects that are now dead. Invoking the lambda after that is undefined behavior.
Here's an example of the potential problems
class A
{
public:
A() :
s("dummy"),
f([&, this]{ std::cout << this << '\n'; return s == "dummy"; })
{}
std::string s;
std::function<bool ()> f;
};
int main()
{
std::vector<A> as;
{
A a;
a.f();
as.push_back(a); // make a copy of a
as[0].f(); // this pointer stays the same as the original
} // lifetime of a ends
as[0].f(); // undefined behavior
}
Upvotes: 9