Skip to content

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

Open in Compiler Explorer ↗ Quiz this entry