78 Advanced The Hundred
[=] Captures this, Not Members
a copy capture that copies less than you think
struct Greeter {
std::string name;
std::function<void()> make_greeting() {
return [=]() { std::cout << "hello, " << name << "\n"; };
}
};
int main() {
auto* alice = new Greeter{"Alice"};
auto greet = alice->make_greeting(); // snapshots the name... right?
greet();
delete alice;
auto* bob = new Greeter{"Bob"};
greet();
delete bob;
}
Run it. What does it print?
Answer
hello, Alice — then hello, Bob. Alice's lambda greets Bob.
Why¶
Inside a member function, [=] does not copy members — it captures the this
pointer, and name in the lambda body quietly means this->name. So the lambda
borrows the Greeter instead of snapshotting it, and once delete alice runs it holds a
dangling pointer: calling it is undefined behavior. What you see above is the
allocator's sense of humor — glibc hands the just-freed chunk straight to
new Greeter{"Bob"}, so the dangling this now points at Bob. That is typical output
(GCC on x86-64), not a guarantee: drop the second new and the same call typically
prints junk, segfaults — or appears to work. This is the member-variable sibling of
entry 66, where a lambda outlives a captured local. C++17's -Wall -Wextra is
silent, but C++20 deprecated implicit this capture via [=] for exactly this reason —
with -std=c++20, GCC warns implicit capture of 'this' via '[=]' is deprecated in
C++20 [-Wdeprecated] and suggests the fix.
The fix¶
Capture what you actually mean:
return [name = name] { std::cout << "hello, " << name << "\n"; }; // copies the member
return [*this] { std::cout << "hello, " << name << "\n"; }; // C++17: copies the object
Takeaway: [=] copies locals but only borrows *this — capture members (or
*this) explicitly whenever a lambda may outlive the object.
Try it: g++ -std=c++17 main.cpp -o demo && ./demo