如何将函数和参数包转换为lambda,转换为std :: thread的构造函数?

时间:2015-01-03 05:13:29

标签: c++ multithreading c++11 lambda perfect-forwarding

我希望在this answer的行中实现C ++ 11中的“可中断”线程。

我已经参数化了我的可中断类,以便它可以包装任何具有类似构造函数的线程(用于std :: thread,但也应该用于类似boost :: thread(nevermind boost :: thread)有这个功能))。那部分无所谓。

尽管链接的答案有一些我必须纠正的问题,但这对我来说很有道理,所以我想知道我哪里出错了。

我已经包含了相关的来源和结果。

interruptible.hpp

#include <atomic>
#include <exception>
#include <thread>

class interrupted_exception : public virtual std::exception
{
public:
    char const *what() const noexcept { return "interrupted"; }
};


template <typename T>
class interruptible
{
public:
    template <typename F, typename... A>
    interruptible(F&& Function, A&&... Arguments) :
        Interrupted(false),
        Thread(
            [](std::atomic_bool *Interrupted, F&& Function, A&&... Arguments)
            {
                LocalInterrupted = Interrupted;
                Function(std::forward<A>(Arguments)...);
            },
            &this->Interrupted,
            std::forward<F>(Function),
            std::forward<A>(Arguments)...
        )
    { }

    void interrupt()            { this->Interrupted = true; }
    bool interrupted() const    { return this->Interrupted; }

    T *operator->()             { return &this->Thread; }

    static inline void check() noexcept(false)
    {
        if (!interruptible::LocalInterrupted)
            return;

        if (!interruptible::LocalInterrupted->load())
            return;

        throw interrupted_exception();
    }

private:
    static thread_local std::atomic_bool *LocalInterrupted;

    std::atomic_bool    Interrupted;
    T                   Thread;
};

template <typename T>
thread_local std::atomic_bool *interruptible<T>::LocalInterrupted = nullptr;

的main.cpp

#include <iostream>
#include <unistd.h>
#include <thread>

#include "interruptible.hpp"

void DoStuff()
{
    try
    {
        while (true)
        {
            std::cout << "Loop" << std::endl;

            sleep(1);

            interruptible<std::thread>::check();
        }
    }
    catch (interrupted_exception const &e)
    {
        std::cout << "Interrupted!" << std::endl;
    }
}


int main()
{
    interruptible<std::thread> a(DoStuff);

    sleep(2);

    std::cout << "Interrupting..." << std::endl;
    a.interrupt();

    sleep(2);

    a->join();

    return 0;
}

当我用g++ -std=c++11 main.cpp(gcc 4.9.2)编译时,我得到:

/usr/include/c++/4.9.2/functional:1665:61: error: no type named ‘type’ in ‘class std::result_of<interruptible<T>::interruptible(F&&, A&& ...) [with F = void (&)(); A = {}; T = std::thread]::<lambda(std::atomic_bool*, void (&)())>(std::atomic_bool*, void (*)())>’
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/include/c++/4.9.2/functional:1695:9: error: no type named ‘type’ in ‘class std::result_of<interruptible<T>::interruptible(F&&, A&& ...) [with F = void (&)(); A = {}; T = std::thread]::<lambda(std::atomic_bool*, void (&)())>(std::atomic_bool*, void (*)())>’
         _M_invoke(_Index_tuple<_Indices...>)
         ^

任何人都可以解决这个问题的任何亮点都将非常感激!

1 个答案:

答案 0 :(得分:2)

std::thread存储传递给其构造函数的参数的衰减副本。 对函数的引用会经历函数到指针衰减,这意味着对于DoStuff参数,传递给lambda的实际类型是{{1}虽然它的void(*)()仍然期望衰变发生前的类型参数,即operator()(推导void(&)())。一旦您尝试使用它们,F参数包中的任何其他参数都会出现同样的问题。

传递A中包含的对象在此上下文中不是一个选项,因为引用包装器可能比存储它的实例的生命周期更长(实际上你不知道什么是std::ref和{ {1}},这些可能是prvalue temporaries,其生命周期在调用Arguments的构造函数后结束。有人在调用 Function的构造函数时决定在interruptible 中包装一些实例,当一个人知道某个对象的生命周期时,这可能是合理的,但这不是这个案例在这里。

如果要最小化将参数从std::ref转移到lambda表达式然后再转移到interruptible对象所需的副本数量和移动量,则可以选择将构造函数重新编译为如下:

interruptible