我需要将结构与已删除的副本构造函数绑定到一个函数.我把我想要实现的目标减少到如下几个最小的例子中:
struct Bar { int i; Bar() = default; Bar(Bar&&) = default; Bar(const Bar&) = delete; Bar& operator=(const Bar&) = delete; }; void foo(Bar b) { std::cout << b.i << std::endl; } int main() { Bar b; b.i = 10; std::function<void()> a = std::bind(foo,std::move(b)); // ERROR a(); return 0; }
从编译器我只会哭泣和咬牙:
test.cpp:22:27: error: no viable conversion from 'typename _Bind_helper<__is_socketlike<void (&)(Bar)>::value,void (&)(Bar),Bar>::type' (aka '_Bind<__func_type (typename decay<Bar>::type)>') to 'std::function<void ()>' std::function<void()> a = std::bind(foo,std::move(b)); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/functional:2013:7: note: candidate constructor not viable: no known conversion from 'typename _Bind_helper<__is_socketlike<void (&)(Bar)>::value,Bar>::type' (aka '_Bind<__func_type (typename decay<Bar>::type)>') to 'nullptr_t' for 1st argument function(nullptr_t) noexcept ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/functional:2024:7: note: candidate constructor not viable: no known conversion from 'typename _Bind_helper<__is_socketlike<void (&)(Bar)>::value,Bar>::type' (aka '_Bind<__func_type (typename decay<Bar>::type)>') to 'const std::function<void ()> &' for 1st argument function(const function& __x); ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/functional:2033:7: note: candidate constructor not viable: no known conversion from 'typename _Bind_helper<__is_socketlike<void (&)(Bar)>::value,Bar>::type' (aka '_Bind<__func_type (typename decay<Bar>::type)>') to 'std::function<void ()> &&' for 1st argument function(function&& __x) : _Function_base() ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/5.1.0/../../../../include/c++/5.1.0/functional:2058:2: note: candidate template ignored: substitution failure [with _Functor = std::_Bind<void (*(Bar))(Bar)>]: no matching function for call to object of type 'std::_Bind<void (*(Bar))(Bar)>' function(_Functor); ^ 1 error generated.
所以我想询问是否有任何解决方法,允许我绑定到foo,同时保持Bar移动.
编辑:
还要考虑以下代码,其中变量b的生命在a被调用之前终止:
int main() { std::function<void()> a; { Bar b; b.i = 10; a = std::bind(foo,std::move(b)); // ERROR } a(); return 0; }
解决方法
std :: function不能采取move-only调用.它将传入的类型删除,以调用(带签名),destroy和copy.1
只写一个move-only std ::函数只是一点点工作. Here is a stab at it在不同的上下文中. live example.
std :: packed_task有趣的是一个移动型的橡皮擦调用者,但它比你想要的更重,并且获得的价值是一种痛苦.
一个更简单的解决方案是滥用一个共享指针:
template<class F> auto shared_function( F&& f ) { auto pf = std::make_shared<std::decay_t<F>>(std::forward<F>(f)); return [pf](auto&&... args){ return (*pf)(decltype(args)(args)...); }; }
它将一些可调用对象包装到共享指针中,将其置于lambda完美转发lambda中.
这说明了一个问题 – 呼叫不起作用!所有上述都有一个const调用.
你想要的是一个只能打一次的任务.
template<class Sig> struct task_once; namespace details_task_once { template<class Sig> struct ipimpl; template<class R,class...Args> struct ipimpl<R(Args...)> { virtual ~ipimpl() {} virtual R invoke(Args&&...args) && = 0; }; template<class Sig,class F> struct pimpl; template<class R,class...Args,class F> struct pimpl<R(Args...),F>:ipimpl<R(Args...)> { F f; template<class Fin> pimpl(Fin&&fin):f(std::forward<Fin>(fin)){} R invoke(Args&&...args) && final override { return std::forward<F>(f)(std::forward<Args>(args)...); }; }; // void case,we don't care about what f returns: template<class...Args,class F> struct pimpl<void(Args...),F>:ipimpl<void(Args...)> { F f; template<class Fin> pimpl(Fin&&fin):f(std::forward<Fin>(fin)){} void invoke(Args&&...args) && final override { std::forward<F>(f)(std::forward<Args>(args)...); }; }; } template<class R,class...Args> struct task_once<R(Args...)> { task_once(task_once&&)=default; task_once&operator=(task_once&&)=default; task_once()=default; explicit operator bool() const { return static_cast<bool>(pimpl); } R operator()(Args...args) && { auto tmp = std::move(pimpl); return std::move(*tmp).invoke(std::forward<Args>(args)...); } // if we can be called with the signature,use this: template<class F,class R2=R,std::enable_if_t< std::is_convertible<std::result_of_t<F&&(Args...)>,R2>{} && !std::is_same<R2,void>{} >* = nullptr > task_once(F&& f):task_once(std::forward<F>(f),std::is_convertible<F&,bool>{}) {} // the case where we are a void return type,we don't // care what the return type of F is,just that we can call it: template<class F,class=std::result_of_t<F&&(Args...)>,std::enable_if_t<std::is_same<R2,void>{}>* = nullptr > task_once(F&& f):task_once(std::forward<F>(f),bool>{}) {} // this helps with overload resolution in some cases: task_once( R(*pf)(Args...) ):task_once(pf,std::true_type{}) {} // = nullptr support: task_once( std::nullptr_t ):task_once() {} private: std::unique_ptr< details_task_once::ipimpl<R(Args...)> > pimpl; // build a pimpl from F. All ctors get here,or to task() eventually: template<class F> task_once( F&& f,std::false_type /* needs a test? No! */ ): pimpl( new details_task_once::pimpl<R(Args...),std::decay_t<F>>{ std::forward<F>(f) } ) {} // cast incoming to bool,if it works,construct,otherwise // we should be empty: // move-constructs,because we need to run-time dispatch between two ctors. // if we pass the test,dispatch to task(?,false_type) (no test needed) // if we fail the test,dispatch to task() (empty task). template<class F> task_once( F&& f,std::true_type /* needs a test? Yes! */ ): task_once( f?task_once( std::forward<F>(f),std::false_type{} ):task_once() ) {} };
请注意,您只能使用上述task_once在rvalue上下文中调用().这是因为()是破坏性的,因为它应该在你的情况下.
可惜上述依赖于C 14.而且我现在不喜欢写C11代码.所以,这是一个更简单的C 11解决方案,性能较差:
std::function<void()> a; { Bar b; b.i = 10; auto pb = std::make_shared<Bar>(std::move(b)); a = [pb]{ return foo(std::move(*pb)); }; } a();
这将b的移动副本推送到共享指针中,将其存储在std ::函数中,然后在第一次调用()时破坏性地消耗它.
1它实现没有它的移动(除非它使用小功能优化,我希望它使用类型的移动).它还实现了转换为原始类型,但每种类型都支持.对于某些类型,它支持check-for-null(即,明确地转换为bool),但我真的不确定它所做的确切类型.