Reputation: 64975
Is it allowed to delete a lambda object while executing the associated lambda function, as long as precautions are taken not to access any of the captured state after the deletion?
Consider the following example:
int main() {
int x = 1;
std::function<void()> d;
auto l = new auto([x, &d]() {
fmt::print("x is {}\n", x);
d();
fmt::print("I'm deleted\n");
});
d = [l](){ delete l; };
(*l)();
}
Here, the lambda pointed to by l
deletes itself using a Rube Goldberg-like approach through the d
function1. After the deletion it prints a fixed message which doesn't access any captured state.
Is it defined behavior?
1 I couldn't figure out a better way to break the circular dependency between the lambda code which needs to see something that points to itself: I'm open to better options.
Upvotes: 2
Views: 746
Reputation: 238401
There might not be an explicit specification for this in the standard.
Although not specified to be so, lambda types are "essentially" like special classes. Staying with this analogy, the question would be same as "Is it well defined to delete this;
". The typical answer to that question is:
As long as you’re careful, it’s okay (not evil) for an object to commit suicide (delete this).
Here’s how I define “careful”:
- You must be absolutely 100% positively sure that this object was allocated via new (not by new[], nor by placement new, nor a local object on the stack, nor a namespace-scope / global, nor a member of another object; but by plain ordinary new).
- You must be absolutely 100% positively sure that your member function will be the last member function invoked on this object.
- You must be absolutely 100% positively sure that the rest of your member function (after the delete this line) doesn’t touch any piece of this object (including calling any other member functions or touching any data members). This includes code that will run in destructors for any objects allocated on the stack that are still alive.
- You must be absolutely 100% positively sure that no one even touches the this pointer itself after the delete this line. In other words, you must not examine it, compare it with another pointer, compare it with nullptr, print it, cast it, do anything with it.
To translate these points to lambdas, "call a member function" becomes "call the lambda", "any data members" becomes "any capture". Your example satisfies all of the points.
Upvotes: 2