Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Non-capturing lambda lifecycle

Tags:

c++

lambda

I'm having a trouble finding a definitive answer if the following code is correct in both cases, with non-capturing lambda and with call of a static function. I'm pretty sure the latter is fine, what I'm concerned about is the lifetime of a lambda when "defined" inside another block/function call, etc. The code seems to work fine but I'd still prefer to know why... Does the non-capturing lambda end up defined as some kind of anonymous (hidden) global symbol so it's basically the same as that static function?

#include <cstdio>
class Listener {
public:
    void event() { printf("event() called\n"); }

    static void forwardEvent(void* userArg) { static_cast<Listener*>(userArg)->event(); }
};

class Producer {
    using Listener = void(void* arg);
    Listener* listener_;
    void* userArg_;

public:
    void attachListener(Listener* listener, void* userArg) {
        listener_ = listener;
        userArg_ = userArg;
    }

    void issueEvent() { listener_(userArg_); }
};

int main() {
    Listener listener;
    Producer producer;

    {  // different scope, in case that makes a difference...
        producer.attachListener(Listener::forwardEvent, &listener);
    }
    producer.issueEvent();

    {  // different scope, in case that makes a difference...
        producer.attachListener([](void* userArg) { static_cast<Listener*>(userArg)->event(); }, &listener);
    }
    producer.issueEvent();
}
like image 643
vaind Avatar asked Oct 15 '25 14:10

vaind


1 Answers

Okay, so your nested Listener definition is this.

using Listener = void(void* arg);
Listener* listener_;

Basically, you deal with good old function pointers. Now when you pass an argument for the function pointer, you do this

producer.attachListener([](void* userArg) { static_cast<Listener*>(userArg)->event(); }, &listener);

The non-capturing lambda is converted to a function pointer via an implicit conversion. This function pointer is

  1. guaranteed to be valid, and points at a function that
  2. behaves exactly the same as if the lambda's operator() was called.

Functions "exist" for the entire duration of the program, and their addresses never become dangling. So your code has well-defined behavior as far as using the pointer obtained from the lambda expression is concerned.

like image 71
StoryTeller - Unslander Monica Avatar answered Oct 17 '25 03:10

StoryTeller - Unslander Monica