Недействительная инициализация не-const ссылки с потоком С++ 11?
Я получаю сообщение об ошибке
ошибка: недействительная инициализация неконстантной ссылки типа 'int & из rvalue типа 'int
из
#include <thread>
#include <iostream>
using namespace std;
void func(int& i){
cout<<++i<<endl;
}
int main(){
int x=7;
thread t(func,x);
t.join();
return 0;
}
Я понимаю, что я не могу сделать thread(func, 4)
, но x
- это переменная, а не временная.
Я использую gcc 4.7 с -std = С++ 11 -pthread
Зачем возникает эта ошибка?
Ответы
Ответ 1
В спецификации конструктора std::thread
указано
Эффекты: Создает объект типа thread. Новый поток выполнения выполняет INVOKE (
DECAY_COPY ( std::forward<F>(f)),
DECAY_COPY (std::forward<Args>(args))...)
, когда вызовы DECAY_COPY оцениваются в потоке конструирования.
Где DECAY_COPY (x) означает вызов decay_copy(x)
, где это определено как:
template <class T> typename decay<T>::type decay_copy(T&& v)
{ return std::forward<T>(v); }
Это означает, что аргументы "распадаются" и копируются, что означает, что они пересылаются по значению и теряют любую cv-квалификацию. Поскольку целевая функция, запускаемая потоком, хочет взять свой параметр по ссылке, вы получите ошибку компилятора, указывающую, что ссылка не может привязываться к объекту, переданному значением.
Это по дизайну, так что по умолчанию локальные переменные, переданные в std::thread
, передаются по значению (т.е. скопированы) не по ссылке, так что новый поток не будет обвиснуть ссылками на локальные переменные, выходящие за пределы области, что приводит к поведению undefined.
Если вы знаете, что безопасно передавать переменные по ссылке, вам нужно сделать это явно, используя reference_wrapper
, на который не повлияет семантика "распада", и пересылает переменную по ссылке на целевой объект, Вы можете создать reference_wrapper
с помощью std::ref
.
Ответ 2
Оберните x
в std::ref
при создании потока.
Если каждый раз, когда вы создавали std::thread
, он принимал все переменные по ссылке, подумал, что произойдет: если вы передадите локальную переменную stack-in, это будет обвисшая ссылка, и поведение undefined приведет к тому, поток выдержал объем этой переменной автоматического хранения. На практике это будет много, и это приведет к многочисленным ошибкам. Вместо этого std::thread
по умолчанию принимает (маршалы через совершенную переадресацию) все аргументы (включая переменные) по значению.
std::future
может бесшумно работать при вызове вашей рабочей функции путем передачи в локальную LLOW-копию x
, но это было бы довольно запутанно: рабочая задача изменила бы x
, который, по вашему мнению, вы передали ссылку, и она не будет отображаться в x
вне задачи. Вместо этого он помогает вам получить это сообщение об ошибке. Вы должны благодарить своих счастливых звезд за это!
Чтобы указать, что вы действительно действительно хотите не принимать что-то по значению, вы завершаете его в std::ref
, и теперь он передается полностью рабочей функции в качестве ссылки. В этом случае вы отвечаете за управление временем жизни ссылки, чтобы указанные данные продолжались, по крайней мере, до тех пор, пока рабочая задача std::future
нуждается в ней.