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

Это пример:

#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 флаг.

Это ожидаемое поведение?

Эта программа передачи данных бесплатно?

Спасибо

3 ответа

Решение

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

Механизм вывода типа аргумента шаблонной функции C++ выводит тип T из аргумента типа T&, Все аргументы std::thread копируются, а затем передаются в функцию потока, чтобы f1() а также f2() всегда используйте эту копию.

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

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

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

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

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

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

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

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

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

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

Больше информации здесь.

Другие вопросы по тегам