【问题标题】:Implementing a function that perfect-forwards to std::thread实现完全转发给STD :: Thread的函数
【发布时间】:2015-05-03 10:09:50
【问题描述】:

我正在尝试围绕 std::thread 编写一个包装器:

#include <thread>
#include <iostream>

struct A {};

template <typename F, typename... Args>
void lifted_lambda_1(void *m, F &&entrypoint,  Args&&... args) {
    std::cout << "I will do something with the void * " << m << std::endl;
    entrypoint(std::forward<Args>(args)...);
}


template <typename F, typename... Args>
void make_thread(void *p, F &&f, Args && ... args) {
    std::thread(lifted_lambda_1<typename std::decay<F>::type, Args...>, p, std::forward<F>(f), std::forward<Args>(args)...).detach();
}

int main() {
    A a;
    make_thread(nullptr, [](A x){}, a);
}

但是当我编译它时,我得到一个错误:

In file included from /usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/thread:39:0,
                 from bubu.cpp:1:
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/functional: In instantiation of ‘struct std::_Bind_simple<void (*(void*, main()::__lambda0, A))(void*, main()::__lambda0&&, A&)>’:
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/thread:137:47:   required from ‘std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&)(void*, main()::__lambda0&&, A&); _Args = {void*&, main()::__lambda0, A&}]’
bubu.cpp:15:132:   required from ‘void make_thread(void*, F&&, Args&& ...) [with F = main()::__lambda0; Args = {A&}]’
bubu.cpp:20:38:   required from here
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/functional:1697:61: error: no type named ‘type’ in ‘class std::result_of<void (*(void*, main()::__lambda0, A))(void*, main()::__lambda0&&, A&)>’
       typedef typename result_of<_Callable(_Args...)>::type result_type;
                                                             ^
/usr/local/sqream-prerequisites/package-install/gcc-4.8.2/include/c++/4.8.2/functional:1727:9: error: no type named ‘type’ in ‘class std::result_of<void (*(void*, main()::__lambda0, A))(void*, main()::__lambda0&&, A&)>’
         _M_invoke(_Index_tuple<_Indices...>)

这个错误的原因是什么?我该如何解决?

【问题讨论】:

  • 试试bind? std::thread(std::bind(lifted..., p, f, args...))
  • 你需要衰减Args:lifted_lambda_1&lt;std::decay_t&lt;F&gt;, std::decay_t&lt;Args&gt;...&gt;
  • @0x499602D2 - 但 gcc 4.8.2 有一个错误,会阻止可变参数在 lambda 中正确展开
  • std::thread 的构造函数会衰减 - 无论如何都会复制它的参数,因此如果将参数传递给它的构造函数,就没有完美的转发。

标签: c++ c++11 gcc variadic-templates stdthread


【解决方案1】:

std::thread 将始终衰减其参数(原因在上述 cmets 之一的链接中给出)。您可以使用reference_wrapper 来保护引用,以便通过左值引用传递参数。

要同时使用左值和右值参数,您需要一个包装函数,它将左值包装在reference_wrapper 中,但允许复制(或移动)右值并将其作为右值转发。这将不是“完美”转发,因为右值将被复制,而不是作为右值引用转发,因此目标函数会被新对象调用。

所以你可以使用这样的东西来有条件地包装左值,但只是转发右值:

template<typename T>
std::reference_wrapper<std::remove_reference_t<T>>
wrap(T& t) { return std::ref(t); }

template<typename T>
T&&
wrap(typename std::remove_reference<T>::type&& t)
{ return std::move(t); }

remove_reference 用于第二个重载,因此 T 处于非推导上下文中,因此参数不是转发引用)。

然后将其用作线程构造函数的参数:

std::thread(lifted_lambda_1<typename std::decay<F>::type, Args...>, p,
            std::forward<F>(f), wrap<Args>(args)...).detach();
                                /*^^^^^^^^^^^^*/

但是,这样做会带回 std::thread 试图通过复制其参数来避免的所有问题!您必须确保在线程完成运行之前传递给make_thread 的任何左值都不会超出范围。由于您要分离线程,因此通常很难做到。使用此功能时必须非常小心。

您可能会编写自己的类模板,其行为类似于保护右值引用的reference_wrapper,以避免创建新对象,但您还必须注意线程函数的右值参数不要超出线程完成运行之前的作用域(如果它们是右值,那么它们很可能是临时的,不会超过创建新线程的调用!)

这里是龙。

【讨论】:

    猜你喜欢
    • 2014-11-25
    • 2017-03-22
    • 1970-01-01
    • 2020-12-09
    • 2017-05-19
    • 1970-01-01
    • 2020-12-15
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多