Because std::function
is copyable, the standard requires that callables used to construct it also be copyable:
n337 (20.8.11.2.1)
template<class F> function(F f);
Requires:
F
shall be CopyConstructible.f
shall be Callable (20.8.11.2) for argument typesArgTypes
and return typeR
. The copy constructor and destructor of A shall not throw exceptions.`
This implies that it is not possible to form an std::function
from a non-copyable bind object or a lambda that captured a move-only type such as std::unique_ptr
.
It seems possible to implement such a move-only wrapper for move-only callables. Is there a standard library move-only equivalent for std::function
or, is there a common workaround for this problem?
No, there is no move-only version of std::function
in the C++ std
library. (As of C++14)
Fastest possible delegates is an implementation of a std::function
like class that happens to be faster than most std::function
implementations in many std
libraries, and it should be easy to fork into a move
and copy
version.
Wrapping your move
only function object into a shared_ptr<F>
in a class with a forwarding operator()
is another approach.
Here is a task
sketch:
template<class Sig>
struct task;
namespace details {
template<class Sig>
struct task_iimpl;
template<class R, class...Args>
struct task_iimpl<R(Args...)> {
virtual ~task_iimpl() {}
virtual R invoke(Args&&...args) const = 0;
};
template<class F, class Sig>
struct task_impl;
template<class F, class R, class...Args>
struct task_impl<F,R(Args...)>:
task_iimpl<R(Args...)>
{
F f;
template<class T>
task_impl(T&& t):f(std::forward<T>(t)) {}
virtual R invoke(Args&&...args) const override {
return f( std::forward<Args>(args...) );
}
};
template<class F, class...Args>
struct task_impl<F,void(Args...)>:
task_iimpl<void(Args...)>
{
F f;
template<class T>
task_impl(T&& t):f(std::forward<T>(t)) {}
virtual void invoke(Args&&...args) const override {
f( std::forward<Args>(args...) );
}
};
}
template<class R, class...Args>
struct task<R(Args...)> {
virtual ~task_iimpl() {}
R operator()(Args...args) const {
return pImpl->invoke(std::forward<Args>(args...));
}
explicit operator bool()const{ return static_cast<bool>(pImpl); }
task(task &&)=default;
task& operator=(task &&)=default;
task()=default;
// and now for a mess of constructors
// the rule is that a task can be constructed from anything
// callable<R(Args...)>, destroyable, and can be constructed
// from whatever is passed in. The callable feature is tested for
// in addition, if constructed from something convertible to `bool`,
// then if that test fails we construct an empty task. This makes us work
// well with empty std::functions and function pointers and other tasks
// that are call-compatible, but not exactly the same:
struct from_func_t {};
template<class F,
class dF=std::decay_t<F>,
class=std::enable_if_t<!std::is_same<dF, task>{}>,
class FR=decltype(std::declval<F const&>()(std::declval<Args>()...)),
std::enable_if_t<std::is_same<R, void>{} || std::is_convertible<FR, R>{} >*=0,
std::enable_if_t<std::is_convertible<dF, bool>{}>*=0
>
task(F&& f):
task(
static_cast<bool>(f)?
task( from_func_t{}, std::forward<F>(f) ):
task()
)
{}
template<class F,
class dF=std::decay_t<F>,
class=std::enable_if_t<!std::is_same<dF, task>{}>,
class FR=decltype(std::declval<F const&>()(std::declval<Args>()...)),
std::enable_if_t<std::is_same<R, void>{} || std::is_convertible<FR, R>{} >*=0,
std::enable_if_t<!std::is_convertible<dF, bool>{}>*=0
>
task(F&& f):
task( from_func_t{}, std::forward<F>(f) )
{}
task(std::nullptr_t):task() {}
// overload resolution helper when signatures match exactly:
task( R(*pf)(Args...) ):
task( pf?task( from_func_t{}, pf ):task() )
{}
private:
template<class F,
class dF=std::decay_t<F>
>
task(from_func_t, F&& f):
pImpl( std::make_unique<details::task_impl<dF,R(Args...)>>(
std::forward<F>(f)
)
{}
std::unique_ptr<details::task_iimpl<R(Args...)> pImpl;
};
but it has not been tested or compiled, I just wrote it.
A more industrial strength version would include a small buffer optimization (SBO) to store small callables (assuming they are movable; if not movable, store on heap to allow moving), and a get-pointer-if-you-guess-the-type-right (like std::function
).