2017-07-30 26 views
0

スレッドを生成し、argsをスレッド関数に転送するこの単純な可変テンプレート関数を考えてみましょう。 スレッドコンストラクタでテンプレート置換エラーが発生するのはなぜですか?完全転送による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(); 
} 

コンパイラ出力:それは作品

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

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を持つ引数の転送を包む

。第一に完全に引き渡された議論ではないか?

+0

['thread'のコンストラクタ](http://en.cppreference.com/w/cpp/thread/thread/thread)は、decay_copyの観点から定義されています。 – ildjarn

+0

ildjarnが何を示唆しているかを詳しく調べるには、渡された関数が参照された整数のコピーと共に呼び出されます。そのコピーは期限切れの値です。非const左辺参照にバインドすることはできません。 – StoryTeller

+0

@StoryTellerは、argsに対してstd :: refを使用する唯一の解決策ですか? – Mozbi

答えて

0

デフォルトでは、スレッドは、その引数であるをコピーします。ローカルデータへの参照は、スレッドの存続期間全体で持続する可能性が低い傾向にあるので、代替は狂気です。

したい場合は、あなたが本当にしたい場合は、参照を渡すstd::refでそれをラップ:今、あなたのコードはまだ未定義の動作を示す

test(std::ref(a)); 

sleepはアクセスを同期せず、すべての非同期読み取り/書き込みは単に未定義の動作です。具体的な「非敵対的」たとえば

、コンパイラはaを想定して自由であることはあなたが従事第1の同期動作であるように、すべての非ローカル効果は無視することができ、join後までmainで変わらず、それローカルでは変更されません。

彼らは、この最適化を検出するために失敗することがあり、そしてあなたの未定義の動作は、あなたが起こって起こるしたいものになる場合がありますが、鼻悪魔はを行うことができます。

joinの後にプリントを移動します。


これはC++です。可変ライフタイム管理はプログラマの仕事です。スレッド間の暗黙の参照は、本当に悪い考えです。

関連する問題