Недействительная инициализация не-const ссылки с потоком С++ 11? - программирование
Подтвердить что ты не робот

Недействительная инициализация не-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

Зачем возникает эта ошибка?

4b9b3361

Ответ 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 нуждается в ней.