2015-03-02 2 views
2

Я пытаюсь написать обертку std::thread:Реализация функции, совершенные вперед к StD :: нити

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

Что является причиной этой ошибки? Как это исправить?

+0

Попробуйте использовать 'bind'? 'std :: thread (std :: bind (lifted ..., p, f, args ...))' –

+1

Вам нужно разложить 'Args':' lifted_lambda_1 , std :: decay_t ...> ' – 0x499602D2

+0

@ 0x499602D2 - Если я распадаюсь на« Аргс », разве я не потеряю префектовую пересылку в некоторых случаях? (то есть передача по ссылке станет пропущенной) – tohava

ответ

3

std::thread всегда будет разлагать свои аргументы (по причинам, указанным по ссылке в одном из комментариев выше). Вы можете использовать reference_wrapper для защиты ссылок, чтобы аргументы могли передаваться с помощью ссылки lvalue.

Чтобы выполнить эту работу с аргументами lvalue и rvalue, вам понадобится функция-обертка, которая будет обертывать lvalues ​​в reference_wrapper, но разрешить копирование (или перемещение) rvalues ​​и перенаправление как rvalues. Это будет не быть «идеальной» пересылкой, так как rvalues ​​будут скопированы, а не переадресованы как ссылки rvalue, поэтому целевая функция вызывается с новыми объектами.

Таким образом, вы можете использовать что-то вроде этого условно обернуть lvalues, но только вперед 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 находится в не выводится контексте, и таким образом, чтобы аргумент не является ссылкой для пересылки).

Затем используйте, что для аргументов конструктору резьбы:

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

Однако, делая это возвращает все проблемы, которые std::thread пытается избежать путем копирования его аргументы! Вы должны убедиться, что любые lvalues, прошедшие до make_thread, не будут выходить за пределы области до того, как поток завершится. Поскольку вы отсоединяете нить, это очень сложно сделать в целом. Вы должны быть очень осторожны при использовании этой функции.

Потенциально вы можете написать свой собственный шаблон класса, который ведет себя как reference_wrapper, который защищает ссылки на rvalue, чтобы избежать создания новых объектов, но тогда вы также должны быть осторожны, чтобы аргументы rvalue для функции потока не выходили из сферы действия до того, как поток закончит работать (и если они являются rvalues, существует высокая вероятность того, что они являются временными, которые будут не пережить вызов, который создает новый поток!)

Здесь будут драконы.