Когда вы используете эту форму конструктора thread
:
std::thread t(calculateValue, std::move(p));
затем изнутри p
перемещается во временный объект std::promise
. Это временное значение используется в качестве аргумента calculateValue
независимо от типа аргумента:
void calculateValue(std::promise<int> &&p) // option #1
void calculateValue(std::promise<int> p) // option #2
Перед тем, как поток завершит свое выполнение, временный объект (или параметр p
во втором случае) уничтожается, и это вызывает std::future_error
, так как общее состояние не готово (set_value
не было вызвано).
Однако, если вы используете std::reference_wrapper
:
std::thread t(calculateValue, std::ref(p));
тогда исходное обещание не перемещено, оно все еще существует, и его деструктор не вызывается до конца main
. Что никогда не достигается, так как основной поток будет ждать на fut.get()
.
Итог: Проблема вовсе не связана с формой параметра calculateValue
. Речь идет о том, переходите вы или нет из p
во временное состояние, что, по сути, определяет, уничтожен ли деструктор обещания, связанного с будущим (и их общим состоянием).