dimanche 23 mai 2021

Lambda capture-by-value while transfering ownership

I have a RAII style class which manages the ownership certain resources. Therefore, copy constructor and assignment operator are explicitly deleted, only move variants exist and they move the resource and invalidate the source (reference). So far it has worked fine, but now I would like to move an object of that kind into a std::function which is processed somewhere else, so that this object becomes the ownership carrier, and eventually release the object. Luckily C++14 allows one to use move assignment while capturing by value so it looked like the way to go. Something like:

std::function<void()> act = [temp = move(resCarrier), description]() mutable {
    try {
// hopefully exception-safe
        auto c = make_unique<DataProcessor>(move(temp), description);
        c->doStuff();
    }  catch (...) {}
};
scheduler->RunLater(move(act));

But not so easy. It should work, but it doesn't. Getting messy template errors, eventually ending in this (slightly shorted for clarity).

/usr/include/c++/10/bits/std_function.h:161:6: error: use of deleted function ‘<WHOLE SIGNATURE OF CURRENT FUNCTION>::<lambda()>&)’
...
note: ‘<WHOLE SIGNATURE OF CURRENT FUNCTION>::<lambda()>&)’ is implicitly deleted because the default definition would be ill-formed:
   99 |   std::function<void()> act = [temp = move(resCarrier), description]() mutable
      |                                                                    ^
...
error: use of deleted function ‘TResCarrier::TResCarrier(const TResCarrier&)’

So it looks like the compiler basically ignores move semantics and insists on using the copy constructor while capturing the variables in the lambda declaration. And so far I was not able to convince it to handle it differently. The "best" idea so far is creating another level of redirection around the carried object so that it can be copied and captured by value (like shared_ptr around the resource). But that would be runtime overhead for no good reason.

Am I overlooking something or is there a hidden nasty restriction of type erasure behind std::function or a limitation of the lambda-to-function mapping?

Aucun commentaire:

Enregistrer un commentaire