Подтвердить что ты не робот

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 он должен быть идеальным переадресацией.

4b9b3361

Ответ 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.

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

Вот живой пример.