2017-07-30 75 views
0

考慮這個簡單的可變參數模板函數,該函數產生一個線程並將該參數轉發給線程函數。 爲什麼我在這裏得到線程構造函數的模板替換失敗?通過完美轉發將variadic模板參數作爲std :: thread的參考

std::thread t; 

void test3(int& a) 
{ 
    a = 10; 
} 

template<class ...Args> 
void test(Args&&... args) 
{ 
    t = std::thread(test3, std::forward<Args>(args)...); 
} 

int main() 
{ 
    auto timer = 2s; 

    int a = 1; 
    test(a); 
    std::this_thread::sleep_for(timer); 
    std::cout << a << std::endl; 
    t.join(); 
} 

編譯器輸出:

template argument deduction/substitution failed: 
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/invoke.h: In substitution of 
'template<class _Callable, class ... _Args> constexpr typename 
std::__invoke_result<_Functor, _ArgTypes>::type std::__invoke(_Callable&&, 
_Args&& ...) [with _Callable = void (*)(int&); _Args = {int}]': 
/opt/wandbox/gcc-head/include/c++/8.0.0/thread:233:29: required by 
substitution of 'template<long unsigned int ..._Ind> decltype 
(std::__invoke(_S_declval<_Ind>()...)) std::thread::_Invoker<std::tuple<void 
(*)(int&), int> >::_M_invoke<_Ind ...>(std::_Index_tuple<_Ind1 ...>) [with 
long unsigned int ..._Ind = {0, 1}]' 
/opt/wandbox/gcc-head/include/c++/8.0.0/thread:240:2: required from 
'struct std::thread::_Invoker<std::tuple<void (*)(int&), int> >' 
/opt/wandbox/gcc-head/include/c++/8.0.0/thread:127:22: required from 
'std::thread::thread(_Callable&&, _Args&& ...) [with _Callable = void (&) 
(int&); _Args = {int&}]' 
prog.cc:23:14: required from 'void test(Args&& ...) [with Args = {int&}]' 
prog.cc:43:11: required from here 
/opt/wandbox/gcc-head/include/c++/8.0.0/bits/invoke.h:89:5: error: no type 
named 'type' in 'struct std::__invoke_result<void (*)(int&), int>' 

當我換的,像這樣一個std :: REF參數轉發:

std::thread(test3, std::ref(std::forward<Args>(args)...)); 

它的工作原理。這些論據是否應該首先完美轉發?

+0

['線程的構造函數](http://en.cppreference.com/w/cpp/thread/thread/thread)是根據decay_copy定義的。 – ildjarn

+0

爲了詳細說明ildjarn暗示的是什麼,你傳遞的函數被調用一個引用的整數的副本。並且該副本是即將到期的值。它不能綁定到一個非const的左值引用。 – StoryTeller

+0

@StoryTeller是唯一的解決方案,然後在參數上使用std :: ref? – Mozbi

回答

0

默認線程副本其參數。另一種選擇是瘋狂的,因爲對本地數據的引用往往不可能持續整個線程的生命週期。

如果你想,如果你真的真的想,通過參考與std::ref把它包:

test(std::ref(a)); 

現在,你的代碼仍然表現出不確定的行爲; sleep不會同步訪問,並且所有非同步讀取/寫入都只是未定義的行爲。

對於一個具體的「非敵對」的例子,編譯器是免費的假設amain保持不變,直到後join因爲這是你搞的第一次同步動作,因此,所有非本地的影響可以忽略不計,它不在本地修改。

他們可能無法檢測到此優化,並且您的未定義行爲可能會導致您想要發生的事情,但鼻子​​惡魔可以做任何事情。

所以移動打印後join


這是C++。變量生命週期管理是程序員的工作。隱含在線程之間的參考是一個非常糟糕的主意。