【问题标题】:std::function and std::packaged_task conversionstd::function 和 std::packaged_task 转换
【发布时间】:2013-06-03 04:08:21
【问题描述】:

我正在尝试将std::packaged_task 移动到std::function<void()>std::vector,因为std::packaged_task 具有void operator()( ArgTypes... args ) 重载,它应该可以转换为std::function<void()>,是吗?

这在 MSVC 和 Clang 上都不能编译,MSVC 抱怨无法将 void 转换为 int,clang 抱怨删除了 std::packaged_task 的复制构造函数,不应该在此处调用 std::vector::push_back 的移动版本吗?这是怎么回事,这是bug吗?

int main () 
{
    std::vector<std::function<void()>> vec;
    std::packaged_task<int()> task( [] { return 100; } );
    vec.push_back( std::move(task) );
}

这是 clang 的神秘模板错误消息

In file included from main.cpp:1:
In file included from /usr/bin/../lib/c++/v1/iostream:38:
In file included from /usr/bin/../lib/c++/v1/ios:216:
In file included from /usr/bin/../lib/c++/v1/__locale:15:
In file included from /usr/bin/../lib/c++/v1/string:434:
In file included from /usr/bin/../lib/c++/v1/algorithm:594:
/usr/bin/../lib/c++/v1/memory:2236:15: error: call to deleted constructor of
      'std::__1::packaged_task<int ()>'
              __first_(_VSTD::forward<_Args1>(get<_I1>(__first_args))...)
              ^        ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
/usr/bin/../lib/c++/v1/memory:2414:15: note: in instantiation of function
      template specialization
      'std::__1::__libcpp_compressed_pair_imp<std::__1::packaged_task<int ()>,
      std::__1::allocator<std::__1::packaged_task<int ()> >,
      2>::__libcpp_compressed_pair_imp<const std::__1::packaged_task<int ()> &,
      const std::__1::allocator<std::__1::packaged_task<int ()> > &, 0, 0>'
      requested here
            : base(__pc, _VSTD::move(__first_args), _VSTD::move(__second_args),
              ^
/usr/bin/../lib/c++/v1/functional:996:11: note: in instantiation of function
      template specialization
      'std::__1::__compressed_pair<std::__1::packaged_task<int ()>,
      std::__1::allocator<std::__1::packaged_task<int ()> >
      >::__compressed_pair<const std::__1::packaged_task<int ()> &, const
      std::__1::allocator<std::__1::packaged_task<int ()> > &>' requested here
        : __f_(piecewise_construct, _VSTD::forward_as_tuple(__f),
          ^
/usr/bin/../lib/c++/v1/functional:1035:17: note: in instantiation of member
      function 'std::__1::__function::__func<std::__1::packaged_task<int ()>,
      std::__1::allocator<std::__1::packaged_task<int ()> >, void ()>::__func'
      requested here
    ::new (__p) __func(__f_.first(), __f_.second());
                ^
/usr/bin/../lib/c++/v1/functional:1277:26: note: in instantiation of member
      function 'std::__1::__function::__func<std::__1::packaged_task<int ()>,
      std::__1::allocator<std::__1::packaged_task<int ()> >, void ()>::__clone'
      requested here
            ::new (__f_) _FF(_VSTD::move(__f));
                         ^
/usr/bin/../lib/c++/v1/memory:1681:31: note: in instantiation of function
      template specialization 'std::__1::function<void
      ()>::function<std::__1::packaged_task<int ()> >' requested here
            ::new((void*)__p) _Up(_VSTD::forward<_Args>(__args)...);
                              ^
/usr/bin/../lib/c++/v1/memory:1608:18: note: in instantiation of function
      template specialization 'std::__1::allocator<std::__1::function<void ()>
      >::construct<std::__1::function<void ()>, std::__1::packaged_task<int ()>
      >' requested here
            {__a.construct(__p, _VSTD::forward<_Args>(__args)...);}
                 ^
/usr/bin/../lib/c++/v1/memory:1492:14: note: in instantiation of function
      template specialization
      'std::__1::allocator_traits<std::__1::allocator<std::__1::function<void
      ()> > >::__construct<std::__1::function<void ()>,
      std::__1::packaged_task<int ()> >' requested here
            {__construct(__has_construct<allocator_type, pointer, _Args...>(),
             ^
/usr/bin/../lib/c++/v1/vector:1519:25: note: in instantiation of function
      template specialization
      'std::__1::allocator_traits<std::__1::allocator<std::__1::function<void
      ()> > >::construct<std::__1::function<void ()>,
      std::__1::packaged_task<int ()> >' requested here
        __alloc_traits::construct(this->__alloc(),
                        ^
main.cpp:19:6: note: in instantiation of function template specialization
      'std::__1::vector<std::__1::function<void ()>,
      std::__1::allocator<std::__1::function<void ()> >
      >::emplace_back<std::__1::packaged_task<int ()> >' requested here
        vec.emplace_back( std::move(task) );
            ^
/usr/bin/../lib/c++/v1/future:1956:5: note: function has been explicitly marked
      deleted here
    packaged_task(const packaged_task&) = delete;
    ^
2 errors generated.

【问题讨论】:

  • 能否请您提供确切的错误消息?
  • 这只是两个错误。
  • std::packaged_task&lt;Sig&gt; 只能移动。 std::function&lt;Sig&gt; 仅适用于可复制函子(并且与 Sig 兼容)。

标签: c++ c++11 clang


【解决方案1】:

它应该可以转换为std::function&lt;void()&gt;,是吗?

没有。 function的相关构造函数要求它的参数是CopyConstructible,而packaged_task不是CopyConstructible,它只是MoveConstructible,因为它的拷贝构造函数和拷贝赋值运算符被删除了。这是 function 的一个不幸要求,但对于 function 是可复制的,这是必要的,因为使用类型擦除来抽象掉包装的可调用对象的细节。

直到过程的后期,C++0x 草案才不需要 CopyConstructible,但它被 DR 1287 添加到最终的 C++11 标准中,所以这是我的错,抱歉 ;-) 早期启用的概念草稿已经需要CopyConstructible概念,但是当概念从草稿中删除时,它就丢失了。

【讨论】:

    【解决方案2】:

    我今天遇到了这个确切的问题。在异步服务方面实现同步调用时,显而易见的做法是尝试将 packaged_task 存储在处理程序函数中,以便在异步处理程序完成时为调用者​​的未来做好准备。

    不幸的是,c++11(和 14)不允许这样做。追踪它花费了我将近一天的开发时间,这个过程让我得到了这个答案。

    我想出了一个解决方案 - 用 std::packaged_task 的专业化替代 std::function。

    感谢 yngum 和 Jonathan 发布问题和答案。

    代码:

    // general template form
    template<class Callable>
    struct universal_call;
    
    // partial specialisation to cover most cases
    template<class R, class...Args>
    struct universal_call<R(Args...)> {
        template<class Callable>
        universal_call(Callable&& callable)
        : _impl { std::make_shared<model<Callable>>(std::forward<Callable>(callable)) }
        {}
    
        R operator()(Args&&...args) const {
            return _impl->call(std::forward<Args>(args)...);
        }
    private:
        struct concept {
            virtual R call(Args&&...args) = 0;
            virtual ~concept() = default;
        };
    
        template<class Callable>
        struct model : concept {
            model(Callable&& callable)
            : _callable(std::move(callable))
            {}
            R call(Args&&...args) override {
                return _callable(std::forward<Args>(args)...);
            }
            Callable _callable;
        };
    
        std::shared_ptr<concept> _impl;
    };
    
    // pathalogical specialisation for std::packaged_task - 
    // erases the return type from the signature
    template<class R, class...Args>
    struct universal_call<std::packaged_task<R(Args...)>>
    : universal_call<void(Args...)>
    {
        using universal_call<void(Args...)>::universal_call;
    };
    
    // (possibly) helpful function
    template<class F>
    universal_call<F> make_universal_call(F&& f)
    {
        return universal_call<F>(std::forward<F>(f));
    }
    

    【讨论】:

    • std::unique_ptr&lt;concept&gt; _impl; 在这里似乎比 std::shared_ptr&lt;concept&gt; _impl; 好,因为 unique_ptr 更轻。
    • @prehistoricpenguin 今天,是的。我认为当时 asio 的处理程序没有移动感知(或者我不知道它们是),所以我选择了共享副本解决方案。今天我会毫无疑问地使用 unique_ptr。
    【解决方案3】:

    今天我遇到了类似的问题。 使用 c++14 和 lambda 捕获初始化我们可以编写:

    std::vector<std::function<void()>> vec;
    using task_t = std::packaged_task<int()>;
    task_t task([] { return 100; });
    vec.emplace_back( [t = std::make_shared<task_t>(std::move(task))]() {
        (*t)();
    });
    

    【讨论】:

      猜你喜欢
      • 2016-06-07
      • 1970-01-01
      • 2015-07-09
      • 2014-12-11
      • 1970-01-01
      • 2012-06-25
      • 1970-01-01
      • 2018-11-06
      • 2022-11-04
      相关资源
      最近更新 更多