std :: function和std :: packaged_task转换

时间:2013-06-03 04:08:22

标签: c++ c++11 clang

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

这不能在MSVC和Clang上编译,MSVC抱怨无法将void转换为int,clang抱怨删除了std::function<void()>的复制构造函数,不应该在这里调用std::packaged_task的移动版本吗?发生了什么,这是一个错误吗?

std::vector::push_back

以下是clang的隐藏模板错误消息

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

4 个答案:

答案 0 :(得分:13)

  

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

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

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

答案 1 :(得分:1)

我今天遇到了这个问题。在根据异步服务实现同步调用时,显而易见的事情是尝试将packaged_task存储在处理函数中,以便在异步处理程序完成时使调用者的未来成为可能。

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

我敲了一个解决方案 - std :: function的替代品,带有std :: packaged_task的特化。

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

答案 2 :(得分:0)

您可以创建一个std::function<void()>来调用std::packaged_task::operator()作为解决方法:

#include <functional>
#include <future>
#include <vector>

int main () {
    typedef std::packaged_task<int()> Task;

    std::vector<std::function<void()>> vec;
    Task task( [] { return 100; } );
    vec.emplace_back( std::bind(&Task::operator(), &task) );

    for( auto& t : vec )
        t();
}

使用-pthread进行编译。

答案 3 :(得分: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)();
});