Subsentient
Subsentient

Reputation: 584

What is the lifetime of the target of pointer-to-function pointing to a lambda?

Sorry, it's a long-winded question, but let me break it down:

Does the C++ standard guarantee that:

void (*Ptr)(void) = [] {};
return Ptr;

will still be defined behaviour?

I understand that, for a closure, it will be defined, because that closure object is moved/copied by value; but, while I know a 'regular' function has infinite/no lifetime, does the target of Ptr have the same? Or is it destroyed and recreated with each instantiation of the lambda?

The reason I care is, I can't use lambdas as callbacks if not. I want to know.

Upvotes: 18

Views: 930

Answers (2)

Nicol Bolas
Nicol Bolas

Reputation: 474046

Objects have lifetimes; functions do not. Functions don't live or die; they always exist. As such, a function cannot go "out of scope", nor can the function being pointed to by a previously valid function pointer vanish. Regardless of where they come from, function pointers are always valid.

Now, this ignores dynamic loading and so forth, but that's extra-standard behavior.

The function pointer you get back from a lambda is a function pointer. It's not special or magic. It therefore behaves no differently from any other function pointer.


Is it possible that the result of conversion to void (*)() points to something which invokes a member function bound to some object?

That's a much more complex question. One which the C++17 standard seems rather under-specified about, but which C++20 is much more clear on.

The C++17 standard only says:

the address of a function that, when invoked, has the same effect as invoking the closure type’s function call operator.

What "the same effect" means exactly is the question. One could argue that "the same effect" means doing what the function call operator would have done, executing the same sequence of statements. One could also argue that "the same effect" would mean invoking the closure object itself.

The latter case may sound difficult to implement, but remember that compiler magic can be employed. The closure could return an instance-specific function pointer, allocated by the closure upon request. Or some-such.

The C++20 standard is much more clear, in part thanks to the fact that capture-less lambdas can be default constructed:

the address of a function F that, when invoked, has the same effect as invoking the closure type's function call operator on a default-constructed instance of the closure type.

So as of C++20, the standard makes it clear that invoking the function pointer is independent of the existence of the lambda object which created it.

Upvotes: 10

user3159253
user3159253

Reputation: 17455

a lambda function is just a syntactic sugar for a real function or functor (i.e. an object with operator() and some members, defined usually in construction time). So, such a function or method is statically defined during compilation.

Although the standard may not specify completely an exact implementation way, as @NicolBolas pointed out, it seems that practical implementations follow the strict guideline: a lambda without a context can be converted to a plain function pointer, no intermediate object is created, neither in the place of lambda defition, nor in the place of invocation. I've just checked it (once again) for gcc and clang and I'm almost sure that MSVC does the same.

Note: The rest is about lambdas with contexts, and although for me it seems more interesting and practical case, the question explicitly deals with context-less lambdas.

A context is stored within a lambda (think of a functor object with some meaningful arguments, received on object construction). So, if you pass some references or pointers to the context, these references and pointers (e.g. this) won't automagically prolongate lifetimes of their corresponding entities. That's why you should be extra careful, when you save a lambda in a scope other than it was defined in.

See an example of problems related to lambda and its context scope definition in this resolved issue. Check the fix to see what was done to make stored lambdas with contexts safe.

Upvotes: 2

Related Questions