如何将可变参数args传递给std :: thread?

时间:2016-01-11 21:24:50

标签: c++ multithreading c++11 stl stdthread

我想通过从C ++ 11包装std :: thread类来使用我自己的Thread实现,这样我就能处理我想要的异常。

这是我的包装类:

#include <Types.hpp>
#include <thread>
#include <exception>
#include <functional>

class Thread
{
    private:

        std::exception_ptr exceptionPtr;
        std::thread thread;

    public:

        using Id = std::thread::id;

        using NativeHandleType = std::thread::native_handle_type;

        Thread() noexcept = default;
        Thread(Thread &&t) noexcept :
            exceptionPtr(std::move(t.exceptionPtr)),
            thread(std::move(t.thread))
        {
        }

        Thread &operator =(Thread &&t) noexcept
        {
            exceptionPtr = std::move(t.exceptionPtr);
            thread = std::move(t.thread);
            return *this;
        }

        template<typename Callable, typename... Args>
        Thread(Callable &&f, Args &&... args) :
            exceptionPtr(nullptr),
            thread([&](Callable &&f, Args &&... args)
            {
                try
                {
                    std::once_flag flag;
                    std::call_once(flag, f, args...);
                }
                catch (...)
                {
                    exceptionPtr = std::current_exception();
                }

            }, f, args...)
        {
            if (exceptionPtr != nullptr)
            {
                 std::rethrow_exception(exceptionPtr);
            }
        }

        bool joinable() const noexcept
        {
            return thread.joinable();
        }

        void join()
        {
            thread.join();
        }

        void detach()
        {
            thread.detach();
        }

        Id getId() const noexcept
        {
            return thread.get_id();
        }

        NativeHandleType nativeHandle()
        {
            return thread.native_handle();
        }

        static uint32_t hardwareConcurrency() noexcept
        {
            return std::thread::hardware_concurrency();
        }

        static void wait(Time t)
        {
            std::this_thread::sleep_for(t);
        }
};

如果没有争论,它的效果非常好:

Thread([&]() {  /* do something */ }).detach();

...但如果我尝试传递可变参数:

Thread(&GUI::refreshTask, this, refreshDelay).detach();

...我在编译时遇到错误:

  

buildroot-2014.02 / output / host / usr / i586-buildroot-linux-uclibc / include / c ++ / 4.8.2 / functional:实例化&#39; struct std :: _ Bind_simple)(std :: chrono: :duration&gt;); Args = {CRH :: GUI const,std :: chrono :: duration&gt;&amp;}] :: __ lambda1(void(CRH :: GUI :: )(std :: chrono :: duration&gt;), CRH :: GUI ,std :: chrono :: duration&gt;)&gt;&#39;:   buildroot-2014.02 / output / host / usr / i586-buildroot-linux-uclibc / include / c ++ / 4.8.2 / thread:137:47:需要来自&#39; std :: thread :: thread(_Callable&amp;&amp; ,_Args&amp;&amp; ...)[with _Callable = CRH :: Thread :: Thread(Callable&amp;&amp;,Args&amp;&amp; ...)[with Callable = void(CRH :: GUI :: ) (std :: chrono :: duration&gt;); Args = {CRH :: GUI const,std :: chrono :: duration&gt;&amp;}] :: __ lambda1; _Args = {void(CRH :: GUI :: &amp;)(std :: chrono :: duration&gt;),CRH :: GUI const&amp;,std :: chrono :: duration&gt;&amp; ;}]&#39;   /home/cyril/Documents/crh-2016/src/robot2/../core/Thread.hpp:72:30:来自&CRH :: Thread :: Thread(Callable&amp;&amp;,Args&amp;&amp; amp; ; ...)[with Callable = void(CRH :: GUI :: )(std :: chrono :: duration&gt;); Args = {CRH :: GUI const,std :: chrono :: duration&gt;&amp;}]&#39;   src / core / GUI.cpp:90:57:从这里需要   buildroot-2014.02 / output / host / usr / i586-buildroot-linux-uclibc / include / c ++ / 4.8.2 / functional:1697:61:错误:没有命名的类型&#39; type&#39; in&#39; class std :: result_of)(std :: chrono :: duration&gt;); Args = {CRH :: GUI const,std :: chrono :: duration&gt;&amp;}] :: __ lambda1(void(CRH :: GUI :: )(std :: chrono :: duration&gt;), CRH :: GUI ,std :: chrono :: duration&gt;)&gt;&#39;          typedef typename result_of&lt; _Callable(_ Args ...)&gt; :: type result_type;                                                                ^   buildroot-2014.02 / output / host / usr / i586-buildroot-linux-uclibc / include / c ++ / 4.8.2 / functional:1727:9:错误:没有命名的类型&#39; type&#39; in&#39; class std :: result_of)(std :: chrono :: duration&gt;); Args = {CRH :: GUI const,std :: chrono :: duration&gt;&amp;}] :: __ lambda1(void(CRH :: GUI :: )(std :: chrono :: duration&gt;), CRH :: GUI ,std :: chrono :: duration&gt;)&gt;&#39;            _M_invoke(_Index_tuple&LT; _Indices ...&GT)

可能会更清楚......但对GCC来说要求太高了。

知道如何解决这个问题吗?

解决方案

#include <Types.hpp>
#include <thread>
#include <exception>
#include <functional>

class Thread
{
    private:

        std::exception_ptr exceptionPtr;
        std::thread thread;

    public:

        using Id = std::thread::id;

        using NativeHandleType = std::thread::native_handle_type;

        Thread() noexcept = default;
        Thread(Thread &&t) noexcept :
            exceptionPtr(std::move(t.exceptionPtr)),
            thread(std::move(t.thread))
        {
        }

        Thread &operator =(Thread &&t) noexcept
        {
            exceptionPtr = std::move(t.exceptionPtr);
            thread = std::move(t.thread);
            return *this;
        }

        template<typename Callable, typename... Args>
        Thread(Callable &&f, Args &&... args) :
            exceptionPtr(nullptr),
            thread([&](typename std::decay<Callable>::type &&f, typename std::decay<Args>::type &&... args)
            {
                try
                {
                    std::bind(f, args...)();
                }
                catch (...)
                {
                    exceptionPtr = std::current_exception();
                }

            }, std::forward<Callable>(f), std::forward<Args>(args)...)
        {
        }

        bool joinable() const noexcept
        {
            return thread.joinable();
        }

        void join()
        {
            thread.join();

            if (exceptionPtr != nullptr)
            {
                std::rethrow_exception(exceptionPtr);
            }
        }

        void detach()
        {
            thread.detach();
        }

        Id getId() const noexcept
        {
            return thread.get_id();
        }

        NativeHandleType nativeHandle()
        {
            return thread.native_handle();
        }

        static uint32_t hardwareConcurrency() noexcept
        {
            return std::thread::hardware_concurrency();
        }

        static void wait(Time t)
        {
            std::this_thread::sleep_for(t);
        }
};

1 个答案:

答案 0 :(得分:4)

CallableArgs是转发引用,因此模板参数推导可以使它们成为左值引用或普通类型,具体取决于参数表达式的值类别。

这意味着当您在lambda声明中重用推导出的类型时:

thread([&](Callable&& f, Args&&... args)

参考折叠发挥作用,对于左值参数refreshDelayArgs成为左值参考。

但是,std::thread存储它接收的参数的衰减副本,然后它从内部存储移动到实际处理程序,将存储的对象转换为xvalues。这就是错误告诉你的:处理程序不能用线程试图传入的参数调用。

相反,您可以按如下方式实现它:

template <typename Callable, typename... Args>
Thread(Callable&& f, Args&&... args)
    : exceptionPtr(nullptr)
    , thread([] (typename std::decay<Callable>::type&& f
               , typename std::decay<Args>::type&&... args)
            {
                // (...)
            }
            , std::forward<Callable>(f), std::forward<Args>(args)...)
{
    // (...)
}