2015-04-17 2 views
3

Меня интересуют (и путают) детали построения объекта std::thread. Согласно cppreference, как функция потока, так и все аргументы копируются в некоторую доступную потоку хранилище, а затем вызывают.Детали в процессе создания объекта std :: thread

1) Что именно такое доступное для хранения данных хранилище? Является ли он семантически эквивалентен некоторому потоковому локальному хранилищу, а переменные разрушаются после возврата функции потока?

2) Что такое категория значений аргументов при передаче функции потока? Описание на cppreference указывает, что они передаются как l-значения (в любом случае им даются имена). Мои тесты на GCC и clang кажутся противоположными, т. Е. R-значениями. В частности, следующий код не компилируется:

void f(int& a) { 
    std::cout << ++a << '\n'; 
} 

int main() { 
    std::thread t(&f, 1); 
    t.join(); 
    return 0; 
} 

Он компилирует, если мы изменим f к

void f(int&& a) { 
    std::cout << ++a << '\n'; 
} 

int main() { 
    std::thread t(&f, 1); 
    t.join(); 
    return 0; 
} 

Итак, что же стандарт сказать об этом?

ответ

2

1) Этот бит текста, доступный для потоков, не представлен непосредственно в стандарте. Стандарт просто говорит, что функция вызывается с аргументами, полученными decay_copy.

2) Если вы изучаете decay_copy внимательно, вы увидите, что она возвращает по значению (потому что его возвращаемый тип std::decay чего-то). Таким образом, функция f вызывается с аргументами rvalue (фактически аргументы prvalue).

Если вы хотите передать lvalues ​​(ссылки), вы можете использовать std::ref и std::cref, чтобы обернуть их.

Точная цитата, C++ 11 30.3.1.2/4:

Эффекты: конструирует объект типа thread. Новый поток выполнения выполняет INVOKE(DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) с звонками DECAY_COPY оценивается в потоке конструирования. Любое возвращаемое значение из этого вызова игнорируется. [Примечание: Это означает, что любые исключения, не вызванные вызовом копии f , будут выбрасываться в потоке конструирования, а не в новом потоке. -end примечание] Если вызов INVOKE(DECAY_COPY (std::forward<F>(f)), DECAY_COPY (std::forward<Args>(args))...) завершает с неперехваченным исключением, std::terminate наречется.

DECAY_COPY определено в 30.2.6/1:

В нескольких местах в этом пункте операция DECAY_COPY(x) используется.Все такое использование означает, вызовите функцию decay_copy(x) и использовать результат, где decay_copy определяется следующим образом:

template <class T> typename decay<T>::type decay_copy(T&& v) 
{ return std::forward<T>(v); } 

INVOKE определен в 20.8.2 в значительной степени таким же образом, как cppreference описывает вызов в ссылке, которую вы предоставили.

+1

Итак, новый поток выполняет 'Invoke (DECAY_COPY (Std :: вперед (е)), DECAY_COPY (станд :: вперед (арг)) ...)' 'за исключением того, DECAY_COPY' уже сделано в конструирующем потоке. Я бы сделал вывод о том, что временные файлы, возвращаемые 'DECAY_COPY', разрушаются после возврата« INVOKE », а разрушение выполняется построенным потоком. – Lingxi

+0

Формулировка о хранении, по-видимому, была вдохновлена ​​документами boost.thread («func копируется в хранилище, управляемое внутри библиотеки потоков ...») – Cubbi