Reputation: 372
I'd like to generically "pickle" function calls so they can be executed later. The return type of those functions will always be void
(for now). Something like this:
template<typename F, typename... Args>
std::function<void()>
pickle(F function, Args&&... args) {
return std::bind(F, args...);
}
The problem is, if args
contains a const reference, std::bind
tries to copy construct the value, which is not always desired or even valid if the type lacks a copy constructor. How do I forward the arguments in a way that uses std::ref
for lvalue references and the normal std::forward
for lvalue references?
#include <functional>
class NonCopyable {
public:
NonCopyable() {}
NonCopyable(const NonCopyable&) = delete;
};
template<typename F, typename... Args>
std::function<void()>
pickle(F function, Args&&... args)
{
return std::bind(function, std::forward<Args>(args)...);
}
int main()
{
NonCopyable obj;
auto f = pickle(
[](const NonCopyable&) {},
obj
);
return 0;
}
The above snippet won't compile, complaining about the deleted copy constructor. (I used forward here because someone suggested it, but has since deleted their answer, it seems).
Upvotes: 7
Views: 1852
Reputation: 131887
Overloading, yay.
// also does the correct thing for `T const`
template<class T>
std::reference_wrapper<T> maybe_ref(T& v, int){ return std::ref(v); }
// just forward rvalues along
template<class T>
T&& maybe_ref(T&& v, long){ return std::forward<T>(v); }
template<typename F, typename... Args>
std::function<void()>
pickle(F function, Args&&... args) {
return std::bind(function, maybe_ref(std::forward<Args>(args), 0)...);
}
The int
/long
parameters and 0
argument disambiguate the lvalue case for compilers that find the overloads to be ambiguous, and doesn't do any harm otherwise.
Upvotes: 8
Reputation: 157484
This is a bit ugly (overuse of enable_if
), but it works:
template<typename T> typename std::enable_if<
!std::is_lvalue_reference<T>::value, T &&>::type
forward_as_ref(typename std::remove_reference<T>::type &t) {
return static_cast<T &&>(t);
}
template<typename T> typename std::enable_if<
!std::is_lvalue_reference<T>::value, T &&>::type
forward_as_ref(typename std::remove_reference<T>::type &&t) {
return t;
}
template<typename T> typename std::enable_if<
std::is_lvalue_reference<T>::value,
std::reference_wrapper<typename std::remove_reference<T>::type>>::type
forward_as_ref(T t) {
return t;
}
Here's a version using class template specialization instead:
template<typename T> struct forward_as_ref_type {
typedef T &&type;
};
template<typename T> struct forward_as_ref_type<T &> {
typedef std::reference_wrapper<T> type;
};
template<typename T> typename forward_as_ref_type<T>::type forward_as_ref(
typename std::remove_reference<T>::type &t) {
return static_cast<typename forward_as_ref_type<T>::type>(t);
}
template<typename T> T &&forward_as_ref(
typename std::remove_reference<T>::type &&t) {
return t;
}
Upvotes: 0