Since callable can be an xvalue there is a chance that it gets destroyed before the lambda capture, hence leaving you with a dangling reference in the capture. To prevent that, if an argument is an r-value it needs to be copied.
A working example:
template<class Callable>
auto discardable(Callable&& callable) { // This one makes a copy of the temporary.
return [callable = std::move(callable)]() mutable {
static_cast<void>(static_cast<Callable&&>(callable)());
};
}
template<class Callable>
auto discardable(Callable& callable) {
return [&callable]() mutable {
static_cast<void>(callable());
};
}
You can still face lifetime issues if callable is an l-value reference but its lifetime scope is smaller than that of the lambda capture returned by discardable. So, it may be the safest and easiest to always move or copy callable.
As a side note, although there are new specialised utilities that perfect-forward the value category of the function object, like std::apply, the standard library algorithms always copy function objects by accepting them by value. So that if one overloaded both operator()()& and operator()()&& the standard library would always use operator()()&.