在C++ 14中,lambda表达式可以通过使用捕获初始化器从它们移动来捕获变量.但是,这会使得结果闭包对象不可复制.如果我有一个带有std::function
参数的现有函数(我无法改变),我无法传递闭包对象,因为std::function
构造函数需要给定的函子CopyConstructible
.
#include#include void doit(std::function f) { f(); } int main() { std::unique_ptr p(new int(5)); doit([p = std::move(p)] () { std::cout << *p << std::endl; }); }
这会出现以下错误:
/usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1911:10: error: call to implicitly-deleted copy constructor of '' new _Functor(*__source._M_access<_Functor*>()); ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:1946:8: note: in instantiation of member function 'std::_Function_base::_Base_manager< >::_M_clone' requested here _M_clone(__dest, __source, _Local_storage()); ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/functional:2457:33: note: in instantiation of member function 'std::_Function_base::_Base_manager< >::_M_manager' requested here _M_manager = &_My_handler::_M_manager; ^ test.cpp:10:7: note: in instantiation of function template specialization 'std::function ::function< , void>' requested here doit([p = std::move(p)] () { std::cout << *p << std::endl; }); ^ test.cpp:10:8: note: copy constructor of '' is implicitly deleted because field '' has a deleted copy constructor doit([p = std::move(p)] () { std::cout << *p << std::endl; }); ^ /usr/bin/../lib/gcc/x86_64-linux-gnu/4.8/../../../../include/c++/4.8/bits/unique_ptr.h:273:7: note: 'unique_ptr' has been explicitly marked deleted here unique_ptr(const unique_ptr&) = delete; ^
有合理的解决方法吗?
使用Ubuntu clang版本3.5-1~exp1(trunk)进行测试
有这种方法:
template< typename signature > struct make_copyable_function_helper; template< typename R, typename... Args > struct make_copyable_function_helper<R(Args...)> { template<typename input> std::function<R(Args...)> operator()( input&& i ) const { auto ptr = std::make_shared< typename std::decay<input>::type >( std::forward<input>(i) ); return [ptr]( Args... args )->R { return (*ptr)(std::forward<Args>(args)...); }; } }; template< typename signature, typename input > std::function<signature> make_copyable_function( input && i ) { return make_copyable_function_helper<signature>()( std::forward<input>(i) ); }
我们创建一个指向我们数据的共享指针,然后创建一个可复制的lambda来捕获该共享指针,然后我们将该可复制的lambda包装到std::function
请求的签名中.
在您的情况下,您只需:
doit( make_copyable_function<void()>( [p = std::move(p)] () { std::cout << *p << std::endl; } ) );
更高级的版本推迟了类型擦除,并添加了一层完美的转发以减少开销:
template<typename input> struct copyable_function { typedef typename std::decay<input>::type stored_input; template<typename... Args> auto operator()( Args&&... args )-> decltype( std::declval<input&>()(std::forward<Args>(args)...) ) { return (*ptr)(std::forward<Args>(args)); } copyable_function( input&& i ):ptr( std::make_shared<stored_input>( std::forward<input>(i) ) ) {} copyable_function( copyable_function const& ) = default; private: std::shared_ptr<stored_input> ptr; }; template<typename input> copyable_function<input> make_copyable_function( input&& i ) { return {std::forward<input>(i)}; }
这不需要你传递签名,在一些情况下可以略微提高效率,但使用更加模糊的技术.
在C++ 14中,可以做得更简单:
template< class F > auto make_copyable_function( F&& f ) { using dF=std::decay_t<F>; auto spf = std::make_shared<dF>( std::forward<F>(f) ); return [spf](auto&&... args)->decltype(auto) { return (*spf)( decltype(args)(args)... ); }; }
彻底消除了对助手类型的需求.
如果闭包对象的生命周期不是问题,您可以在引用包装器中传递它:
int main() { std::unique_ptr<int> p(new int(5)); auto f = [p = std::move(p)]{ std::cout << *p << std::endl; }; doit(std::cref(f)); }
这显然不适用于所有场景,但它适用于您的示例程序.
编辑:瞥一眼N3797(C++ 14工作草案)§20.9.11.2.1[func.wrap.func.con] p7,CopyConstructible
要求仍然存在.我想知道是否存在一个不能放松的技术原因MoveConstructible
,或者委员会是否只是没有解决它?
编辑:回答我自己的问题:std::function
是的CopyConstructible
,所以包裹的仿函数也需要CopyConstructible
.