Разница между указателем и ссылкой как параметр потока

Это пример:

#include<iostream>
#include<thread>
using namespace std;

void f1(double& ret) {
   ret=5.;
}

void f2(double* ret) {
   *ret=5.;
}

int main() {
   double ret=0.;
   thread t1(f1, ret);
   t1.join();
   cout << "ret=" << ret << endl;
   thread t2(f2, &ret);
   t2.join();
   cout << "ret=" << ret << endl;   
}

И результат:

ret=0
ret=5

Скомпилирован с gcc 4.5.2 с и без -O2

Является ли это ожидаемым поведением?

Является ли эта гонка данных о программе бесплатной?

Спасибо

Ответ 1

Конструктор std::thread выводит типы аргументов и сохраняет их по значению.

Механизм вывода аргумента типа аргумента типа С++ выводит тип T из аргумента типа T&. Следовательно, все аргументы std::thread передаются по значению, так что f1() и f2() всегда получают копию.

Если вы настаиваете на использовании ссылки, оберните аргумент с помощью boost::ref() или std::ref():

thread t1(f1, boost::ref(ret));

Или, если вы предпочитаете простоту, передайте указатель. Это то, что boost::ref() или std::ref() делают для вас за сценой.

Ответ 2

Для вас требуется, чтобы явный std::ref() (или boost::ref()) в этих ситуациях был очень полезной функцией безопасности, поскольку передача ссылки может быть по своей природе опасной вещью.

С неконстантной ссылкой довольно часто возникает опасность того, что вы передаете локальную переменную, с константой-ссылкой она может быть временной, и поскольку вы создаете функцию, которая будет вызываться в другом потоке ( и с привязкой вообще, часто функцией, которая будет называться позже/асинхронным образом), у вас будет большая опасность того, что объект перестанет быть действительным.

привязка выглядит аккуратно, но эти ошибки сложнее всего найти, так как там, где ошибка улавливается (т.е. при вызове функции), это не то место, где была сделана ошибка (во время привязки), и это может быть очень трудно точно определить, какую функцию вызывается в то время, и поэтому там, где она была связана.

Это безопасно в вашем экземпляре, когда вы присоединяетесь к потоку в области переменной, которую вы передаете в качестве ссылки. Поэтому, когда вы знаете, что это так, есть механизм для передачи ссылки.

Это не особенность языка, который я хотел бы видеть измененным, особенно потому, что, вероятно, существует много существующего кода, на котором он делает копию, которая сломалась бы, если бы она просто взялась за ссылку автоматически (и тогда ей понадобится явный способ принудительного копирования).

Ответ 3

Если вы хотите передать параметры по ссылке на std::thread, вы должны заключить каждый из них в std::ref:

thread t1(f1, std::ref(ret));

Подробнее здесь.