实现一个完美转发到std :: thread的函数 [英] Implementing a function that perfect-forwards to std::thread

查看:818
本文介绍了实现一个完美转发到std :: thread的函数的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想在 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);
}

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

But when I compile it I get an error:

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...>)

此错误的原因是什么?如何解决?

What is the reason for this error? How do I fix it?

推荐答案

std :: thread 衰减它的参数(因为在上面的评论之一的链接中给出的原因)。您可以使用 reference_wrapper 保护引用,以便参数可以通过lvalue引用传递。

std::thread will always decay its arguments (for the reasons given at the link in one of the comments above). You can use reference_wrapper to protect references, so that arguments can be passed by lvalue reference.

对于lvalue和rvalue参数,你需要一个包装函数,它将在$ code> reference_wrapper 中包含lvalue,但允许复制(或移动)rvalues并作为rvalues转发。这将不是是完美的转发,因为右值将被复制,不转发为右值引用,所以目标函数调用与新对象。

To make that work with both lvalue and rvalue arguments you need a wrapper function which will wrap lvalues in reference_wrapper but allow rvalues to be copied (or moved) and forwarded as rvalues. This will not be "perfect" forwarding, because rvalues will be copied, not forwarded as rvalue references, so the target function gets called with new objects.

所以你可以使用这样的东西有条件地封装左值,但只是向前值:

So you can use something like this to conditionally wrap lvalues but just forward rvalues:

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 在非推导的上下文中,并且使得参数不是转发引用)。

(remove_reference is used on the second overload so that T is in a non-deduced context, and so that the argument is not a forwarding reference).

然后使用它为线程构造函数的参数:

Then use that for the arguments to the thread constructor:

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

但是,这样做带来了 std :: thread 尝试通过复制其参数来避免的所有问题!您必须确保传递给 make_thread 的任何lval值在线程完成运行之前不会超出作用域。因为你是分离线程,这是很难做的一般。

However, doing this brings back all the problems that std::thread tries to avoid by copying its arguments! You must ensure that any lvalues passed to make_thread will not go out of scope before the thread finishes running. Since you are detaching the thread, that is very difficult to do in general. You must be very careful when using this function.

潜在地,您可以编写自己的类模板,其行为类似 reference_wrapper 保护引用的值,避免创建新对象,但是你还必须小心,线程函数的右值参数在线程完成运行之前不会超出范围(如果它们是rvalues,则有很高的概率

Potentially you could write your own class template that behaves like reference_wrapper that protects rvalue references, to avoid the new objects being created, but then you must also be careful that the rvalue arguments to the thread function do not go out of scope before the thread finishes running (and if they are rvalues there is a high probability that they are temporaries which will not outlive the call that creates the new thread!)

这里是龙。

这篇关于实现一个完美转发到std :: thread的函数的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆