因为std::function
是可复制的,标准要求用于构造它的可调用对象也是可复制的:
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.`
这意味着不可能形成 std::function
来自不可复制的绑定(bind)对象或捕获仅 move 类型的 lambda,例如 std::unique_ptr
.
似乎可以为只 move 的可调用对象实现这样的只 move 包装器。 std::function
是否有标准库仅 move 等效项?或者,是否有针对此问题的通用解决方法?
最佳答案
不,std::function
没有只能 move 的版本在 C++ std
图书馆。 (从 C++14 开始)
Fastest possible delegates是 std::function
的实现像恰好比大多数 std::function
更快的类许多 std
中的实现库,并且应该很容易 fork 成 move
和 copy
版本。
包装您的move
仅将函数对象转换为 shared_ptr<F>
在一个有转发的类中 operator()
是另一种方法。
这里是 task
草图:
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;
};
但它没有经过测试或编译,我只是写了它。
更工业化的版本将包括一个小型缓冲区优化 (SBO) 来存储小型可调用对象(假设它们是可 move 的;如果不可 move ,则存储在堆上以允许 move ),以及一个 get-pointer-if-you-guess -the-type-right(如 std::function
)。
关于c++ - std::function 的仅 move 版本,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/25330716/