将转发引用传递给线程包装器类

时间:2017-11-17 11:22:28

标签: c++ multithreading c++11

让我们先看一下具体的功能:

void boo1 () { std::cout << "boo1\n"; }
void boo2 (std::string) { std::cout << "boo2\n"; }

struct X {
    void boo3 () { std::cout << "boo3\n"; }
    void boo4 (std::string) { std::cout << "boo4\n"; }
};

我希望用一些&#34; guard&#34;执行这些功能。功能,它做异常保护(下面的解释)。所以,我写了两个函数,一个用于自由函数,另一个用于成员函数:

template <typename C, typename... Args>
typename std::enable_if<!std::is_member_function_pointer<C>::value, void>::type
foo (C&& c, Args&&... args) {
    std::cout << "not memfun\n";
    c (std::forward<Args> (args)...);
}

template <typename C, typename T, typename... Args>
typename std::enable_if<std::is_member_function_pointer<C>::value, void>::type
foo (C&& c, T* o, Args&&... args) {
    std::cout << "memfun\n";
    (o->*c) (std::forward<Args> (args)...);
}

现在我想在新主题中执行boo1()boo2()X::boo3()X::boo4(),但使用&#34; protection&#34;由foo()函数使用。所以,没有任何问题,我可以这样做:

X x;
std::thread th1 {&foo<void (*) ()>, &boo1};
std::thread th2 {&foo<void (*) (std::string), std::string>, &boo2, "Hello"};
std::thread th3 {&foo<void (X::*) (), X>, &X::boo3, &x};
std::thread th4 {&foo<void (X::*) (std::string), X, std::string>, &X::boo4, &x, "Hello"};

所以,更进一步,我已经编写了线程包装器类来停止明确使用std::join()std::detach()。 当析构函数处于运行状态时,会调用其中一个。为了简化这个例子,我只使用了其中一个。 所以,代码看起来像这样:

class Th {
public:
    template <typename C, 
              typename = typename std::enable_if<!std::is_member_function_pointer<C>::value, void>::type,
              typename... Args>
    Th (C&& c, Args... args) // (X)
    : t {foo<C, Args...>, std::forward<C> (c), std::forward<Args> (args)...} {}

    template <typename C,
              typename = typename std::enable_if<std::is_member_function_pointer<C>::value, void>::type,
              typename T,
              typename... Args>
    Th (C&& c, T* o, Args... args) // (Y)
    : t { foo<C, T, Args...>, std::forward<C> (c), o, std::forward<Args> (args)...} {}


    ~Th () { t.join (); }

private:
    std::thread t;
};

使用此实现没有问题,以下用例:

X x;
Th h1 {&boo1};
Th h2 {&boo2, "Hello"};
Th h3 {&X::boo3, &x};
Th h4 {&X::boo4, &x, "Hello"};

除了工作外。然而,这里有一个缺陷 - 没有完美的转发。如果我将所需的(X)(Y) Args...更改为Args&&..., 代码没有编译时出现一些错误,其中一个是:

error: no type named ‘type’ in ‘class std::result_of<void (*(void (*)(std::basic_string<char>), const char*))(void (*&&)(std::basic_string<char>), const char (&)[6])>’

如果我理解正确的话,std::bind()构造函数中的std::thread无法完成工作,或者如果我错了就纠正我。

我的问题是:在这种情况下我该怎样做才能利用完美转发?

用于编译的编译器是GCC 4.8.1。

承诺的解释:我希望foo()个函数围绕c()trycatch的来电,以确保抓取异常,在某种日志记录机制中处理此信息,并根据需要重新抛出异常,并为Th类的用户提供透明度。

1 个答案:

答案 0 :(得分:4)

将左值引用传递给std::thread时,需要将它们包装在std::reference_wrapper中。例如,您可以通过以下方式执行此操作:

namespace detail
{

template<typename T>
std::reference_wrapper<T> forward_to_thread(T& t, std::true_type) {
    return std::ref(t);
}

template<typename T>
T&& forward_to_thread(T&& t, std::false_type) {
    return std::move(t);
}

}

template<typename T>
auto forward_to_thread(T&& t)
    -> decltype(detail::forward_to_thread(std::forward<T>(t), std::is_lvalue_reference<T>{}))
{
    return detail::forward_to_thread(std::forward<T>(t), std::is_lvalue_reference<T>{});
}

然后,您只需在std::forward初始化中将forward_to_thread次来电替换为thread

class Th {
public:
    template <typename C,
              typename = typename std::enable_if<!std::is_member_function_pointer<C>::value, void>::type,
              typename... Args>
    Th (C&& c, Args&&... args) // (X)
    : t {foo<C, Args...>, std::forward<C> (c), forward_to_thread<Args> (std::forward<Args>(args))...} {}

    template <typename C,
              typename = typename std::enable_if<std::is_member_function_pointer<C>::value, void>::type,
              typename T,
              typename... Args>
    Th (C&& c, T* o, Args&&... args) // (Y)
    : t { foo<C, T, Args...>, std::forward<C> (c), o, forward_to_thread<Args> (std::forward<Args>(args))...} {}


    ~Th () { t.join (); }

private:
    std::thread t;
};

这适用于gcc 4.8.2 [link]

如果您可以使用更新的编译器,请考虑使用std::invoke(C ++ 17)获取更多简洁和可读的代码(即this)。