Home > Enterprise >  Do anonymous lambdas maintain their address across calls?
Do anonymous lambdas maintain their address across calls?

Time:02-27

I have some code where I need to create unique-ids in some function to track some objects manipulated by that function. I thought about using the address of a callback which the function expects. The callbacks passed to the function are anonymous lambdas.

I call this function in many different places and, in every place, each lambda maintains its address across calls. That is, even though the lambdas are temporary, each individual lambda has its same address every time it's passed to the function.

This is nice, for my use case. But, why do they have the same address, and not a new one every time? Most importantly, is this reliable across compilers?

Compiler: MSVC 2019

// A method within some class.
void Method() {
    // Helper lambda
    auto makeSignal = [this](State& state, Data& data, int foo, auto&& onClick) {
        DoMakeSignal(state, data, true, foo, 3.14, std::forward<decltype(onClick)>(onClick));
    };
   
    // The unique-id will be the address of the lambda.
    // Everytime this 'Method' is called, this lambda 
    // will have the same address. Nice, but why?
    makeSignal(GetState(), GetData(), 5, [this] {
        log("signal callback");
    });

    makeSignal(GetState(), GetData(), 42, [this] {
        log("signal callback");
    });    
}

// In another file
void DoMakeSignal(State& state, Data&, bool, int, float, auto&& onClick) {
    uintptr_t uid = reinterpret_cast<uintptr_t>(&onClick);
    state.mLastObjectUid = uid;
    // ...
}

CodePudding user response:

That's by pure chance. The lambda object lives only until the end of the makeSignal call and after that the address which was occupied by the object can be reused.

There is no guarantee on whether a new lambda object (or any other object) will have the same address or not.

In particular that also means that after the call to makeSignal, if onClick was stored by-pointer or by-reference, calling it will result in UB, since the object is not alive anymore.


However, each lambda expression has its own type and its own call operator function, both of which are properties that could be used to obtain a unique id to identify the specific lambda expression.


If you want unique ids of lambda expressions, and only lambda expressions, then for example the following should work:

struct id_t {};
template<typename> id_t id_var;
template<typename T> constexpr auto id = &id_var<std::remove_cvref_t<T>>;

//...

state.mLastObjectUid = id<decltype(onClick)>; // type: id_t*
  •  Tags:  
  • c
  • Related