Логично ли это поведение std:: ref?
Рассмотрим этот код:
#include <iostream>
#include <functional>
int xx = 7;
template<class T>
void f1(T arg)
{
arg += xx;
}
template<class T>
void f2(T arg)
{
arg = xx;
}
int main()
{
int j;
j=100;
f1(std::ref(j));
std::cout << j << std::endl;
j=100;
f2(std::ref(j));
std::cout << j << std::endl;
}
При выполнении этот код выводит
107
100
Я бы ожидал, что второе значение будет равным 7, а не 100.
Что мне не хватает?
Ответы
Ответ 1
Небольшая модификация f2
дает ключ:
template<class T>
void f2(T arg)
{
arg.get() = xx;
}
Теперь это делает то, что вы ожидаете.
Это произошло потому, что std::ref
возвращает объект std::reference_wrapper<>
. Оператор присваивания которого перегружает оболочку.
(см. http://en.cppreference.com/w/cpp/utility/functional/reference_wrapper/operator%3D)
Он не присваивает завернутую ссылку.
В случае f1
все работает так, как вы ожидали, потому что std::reference_wrapper<T>
предоставляет оператор преобразования для T&
, который будет связываться с неявной правой частью int
неявный operator+
.
Ответ 2
reference_wrapper
имеет operator =
и не явный конструктор, см. документацию.
Итак, даже если это удивительно, это нормальное поведение:
f2
перенаправляет локальный reference_wrapper на xx
.
Ответ 3
arg = xx;
Локальный arg
теперь относится к (читается как связывание с) xx
. (И больше не относится к j
)
arg += xx;
Неявный operator T& ()
применяется для соответствия аргументу operator +=
и, следовательно, добавление выполняется по упомянутому объекту i.e. j
.
Таким образом, наблюдаемое поведение является правильным.