Std:: bind потерять ссылку при доставке в качестве ссылки на rvalue
У меня есть следующий код:
#include <stdio.h>
#include <functional>
template <typename T>
auto callback(T&& func) ->decltype(func())
{
return func();
}
double test(double& value)
{
value=value+1.0;
return value;
}
int main(void)
{
double t=1.0;
printf("%f\n",t);
test(t);
printf("%f\n",t);
callback(std::bind(test,t));
printf("%f\n",t);
}
И он выводит
1.000000
2.000000
2.000000
Это означает, что функция callback
получила копию t
вместо ссылки на t
. Мне интересно, что произошло, поскольку для std::bind
он должен быть идеальным переадресацией.
Ответы
Ответ 1
std::bind
по умолчанию использует семантику значений. Это нормальное значение по умолчанию, которое позволяет безопасно выполнять следующие действия.
int f(double x);
auto fun = std::bind(f, 1.0); // stores a copy, not a reference to a temporary
fun();
Использование семантики значения безопасно: время жизни связанных аргументов становится временем жизни объекта, возвращаемого bind. Использование эталонной семантики не будет иметь такой гарантии. Поэтому вам нужно быть явным, когда вам нужна эталонная семантика; если у вас проблемы, тогда это ваша вина. Для этого вам нужно использовать std::ref
:
int main(void)
{
double t=1.0;
printf("%f\n",t);
test(t);
printf("%f\n",t);
callback(std::bind(test, std::ref(t)));
printf("%f\n",t);
}
Этот же протокол используется в другом месте в стандартной библиотеке, например конструктор std::thread
.
Ответ 2
std::bind()
предназначен для семантики значения (как Р. Мартиньо Фернандес прекрасно объясняет в своем ответе) и создает копии внутри. Что вам нужно/нужно std::ref
:
callback(std::bind(test, std::ref(t)));
// ^^^^^^^^^^^
std::ref
возвращает объект std::reference_wrapper<>
, который привязывает ссылку к исходному аргументу. Таким образом, объект reference_wrapper
вокруг t
будет скопирован, а не t
.
Это позволяет вам выбирать между семантикой значения (предположительно по умолчанию) и ссылочной семантикой (что требует вашего явного вмешательства).
Вот живой пример.