10

我正在尝试将 astd::packaged_task移入 a std::vectorof std::function<void()>,因为std::packaged_taskvoid operator()( ArgTypes... args )重载,它应该可以转换为std::function<void()>,是吗?

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

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.
4

3 回答 3

14

它应该可以转换为std::function<void()>,是吗?

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

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

于 2013-06-03T08:55:52.117 回答
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));
}
于 2015-06-16T21:59:19.067 回答
0

今天我有类似的问题。使用 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)();
});
于 2020-08-25T19:51:49.313 回答