I'm having trouble figuring out the best way to deal with allocation and deallocation of std::function
.
Of course the underlying function is never going to be deallocated, but the captured variables etc. need to be deallocated.
I presume that this is deallocated inside the destructor of std::function
.
Does this mean the best way to manage functions that persist is through the use of std::unique_ptr<std::function<...>>
and std::shared_ptr<std::function<...>>
, or even just a raw pointer std::function<...>*
?
Wouldn't this be an extra pointer of indirection? Is this the recommended way to go?
CodePudding user response:
Remember that std::function
is not a lambda. A std::function
don't have captures. It's a polymorphic wrapper around a function like object like a lambda.
A (extremely rough and incomlete and incorrect) representation of std::function
looks like this:
template<typename R, typename... Args>
struct function {
function(auto lambda) :
fptr{[](void* l, Args...){ static_cast<decltype(lambda)>(l)(std::forward<Args>(args)...); }}
function_object{new auto(lambda)} {}
auto operator()(Args...) const -> R {
return fptr(function_object, std::forward<Args>(args)...);
}
// other members...
private:
R(*fptr)(void*, Args...);
void* function_object;
};
As you can see, the function_object
member is dynamically allocated. This is the lambda, and the lambda contains the captures. They will only be deleted when the std::function
destructor is called.
Then, also remember that std::function
respects value semantics. This means that if the std::function
is copied, the lambda held inside is also copied. This means you can freely copy, move around, make some copies out of scope, call the function and it won't affect other copies.
This means this code works:
auto make_function_with_captures(double a, double b) -> std::function<double()> {
// captures 'a' and 'b' as values
auto lambda = [a, b] {
return a b;
};
// Here, 'lambda' is copied in the std::function
return std::function<double()>{lambda};
} // 'lambda' goes out of scope here, with its captures
int main() {
auto function = make_function_with_captures(1.2, 4.5);
// Here, use a copy of the lambda that was created in the function
double a = function();
}
Wouldn't this be an extra pointer of indirection?
Yes. This would be two pointer indirection with a virtual call.
Is this the recommended way to go?
I would answer this question to "no" most of the time, like 98% of cases.