如何将可变参数传递给std::线程

How to pass variadic args to a std::thread?

本文关键字:std 参数传递 线程      更新时间:2023-10-16

我想通过包装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-uclbc/include/c++/4.8.2/functional:在"struct-std::_Bind_simple"的实例化中(std::chrono::duration>);Args={CRH::GUI const,std::chrono::duration>&}]::__lambda1(void(CRH:;GUI::)(std::chrono::duration>),CRH::GUI,std:buildroot-2014.02/output/host/usr/i586 buildroot-linux-uclbc/include/c++/4.8.2/thread:137:47:必需自"std::thread::thread(_Callable&&,_Args&…)"[其中_Callable=CRH::thread::thread(可调用&&,Args&…)[其中Callable=void(CRH::GUI::)(std::chrono::duration>);Args={CRH:;GUIconst,std::chrono::持续时间>&}]::__lambda1_Args={void(CRH::GUI::&)(std::chrono::duration>),CRH:;GUI:常量&,std::chrono::持续时间>&}/home/cyrl/Documents/crh-2016/src/robot2//core/Thread.hp:72:30:从"CRH::Thread::Thread(Callable&&,Args&…)[带有Callable=void(CRH:;GUI::)(std::chrono::duration>);Args={CRH:src/core/GUI.cpp:90:57:此处需要buildroot-2014.02/output/host/usr/i586 buildroot-linux-uclbc/include/c++/4.8.2/functional:1697:61:错误:"class std::result_of"中没有名为"type"的类型(std::chrono::duration>);Args={CRH::GUI const,std::chrono::duration>&}]::__lambda1(void(CRH:;GUI::)(std::chrono::duration>),CRH::GUI,std:typedef typename result_of&lt_可调用(_Args…)>::type result_type;^buildroot-2014.02/output/host/usr/i586 buildroot-linux-uclbc/include/c++/4.8.2/functional:11727:9:error:'classstd::result_of)中没有名为'type'的类型(std::chrono::duration>);Args={CRH::GUI const,std::chrono::duration>&}]::__lambda1(void(CRH:;GUI::)(std::chrono::duration>),CRH::GUI,std:_M_invoke(_Index_tuple<_Indices…>)

它可能会更清楚一点。。。但这对海湾合作委员会来说要求太高了。

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

解决方案

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

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

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

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

引用折叠开始发挥作用,并且对于左值参数refreshDelayArgs变成左值引用。

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

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

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)...)
{
    // (...)
}
相关文章: