将不可复制的闭包对象传递给std :: function参数 [英] Passing a non-copyable closure object to std::function parameter

查看:318
本文介绍了将不可复制的闭包对象传递给std :: function参数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在C ++ 14中,lambda表达式可以通过使用捕获初始化器从它们移动来捕获变量。但是,这使得生成的闭包对象不可复制。如果我有一个现有的函数接受 std :: function 参数(我不能改变),我不能传递closure对象,因为 std: :function 的构造函数要求给定的函子 CopyConstructible

In C++14, a lambda expression can capture variables by moving from them using capture initializers. However, this makes the resulting closure object non-copyable. If I have an existing function that takes a std::function argument (that I cannot change), I cannot pass the closure object, because std::function's constructor requires the given functor to be CopyConstructible.

#include <iostream>
#include <memory>

void doit(std::function<void()> f) {
    f();
}

int main()
{
    std::unique_ptr<int> 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 '<lambda at test.cpp:10:7>'
            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<<lambda at test.cpp:10:7>
      >::_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<<lambda at test.cpp:10:7>
      >::_M_manager' requested here
            _M_manager = &_My_handler::_M_manager;
                                       ^
test.cpp:10:7: note: in instantiation of function template specialization 'std::function<void
      ()>::function<<lambda at test.cpp:10:7>, 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)测试

Testing with Ubuntu clang version 3.5-1~exp1 (trunk)

推荐答案

p>

There is this approach:

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 中。

where we make a shared pointer to our data, then make a copyable lambda that captures that shared pointer, then we wrap that copyable lambda into a std::function of the requested signature.

在上面的例子中,你只需要:

In your case above, you'd just:

doit( make_copyable_function<void()>( [p = std::move(p)] () { std::cout << *p << std::endl; } ) );

稍微更高级的版本推迟类型擦除并添加一层完美的转发以减少开销: / p>

A slightly more advanced version defers the type erasure and adds a layer of perfect forwarding to reduce overhead:

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)}; 
}

这不需要您传递签名,

which does not require you to pass the signature in, and can be slightly more efficient in a few cases, but uses more obscure techniques.

在C ++ 14中,这可以更简单:

In C++14 with this can be made even more brief:

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)... );
  };
}

完全取消辅助类型的需要。

doing away with the need for the helper type entirely.

这篇关于将不可复制的闭包对象传递给std :: function参数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆