Проблема в том, что j
передается параметру t1
шаблона flip1
. Этот параметр имеет простой, не ссылочный тип int
, а не int&
. Таким образом, этот вызов создает следующий экземпляр шаблона flip1
:
void flip1(void(*fcn)(int, int&), int t1, int t2);
Значение j
копируется в t1
. Ссылочный параметр в функции f()
связан с t1
, а не с j
.
Чтобы передать ссылку через функцию, необходимо переписать ее так, чтобы параметры сохраняли принадлежность своих аргументов к l-значениям. Немного поразмыслив, можно предположить, что константность аргументов также необходимо сохранить.
Всю информацию о типе аргумента можно сохранить, определив соответствующий ему параметр функции как ссылку на r-значение параметра типа шаблона. Использование ссылочного параметра (l- или r-значение) позволяет сохранить константность, поскольку спецификатор const
в ссылочном типе нижнего уровня. Благодаря сворачиванию ссылок (см. раздел 16.2.5), если определить параметры функции как T1&&
и T2&&
, можно сохранить принадлежность к l- или r-значениям аргументов функции (см. раздел 16.2.5):
template
void flip2(F f, T1 &&t1, T2 &&t2) {
f(t2, t1);
}
Как и прежде, если происходит вызов flip2(f, j, 42)
, l-значение j
передается параметру t1
. Однако в функции flip()
для T1
выводится тип int&
, а значит, тип t1
сворачивается в int&
. Ссылка t1
связана с j
. Когда функция flip()
вызывает функцию f()
, ссылочный параметр v2
в функции f()
привязан к t1
, который, в свою очередь, привязан к j
. Когда функция f()
осуществляет инкремент v2
, это изменяет значение j
.
Т&&
), сохраняет константность и принадлежность к l- или r-значениям соответствующих ему аргументов.
Эта версия функции flip()
решает одну половину проблемы. Она работает прекрасно с функциями, получающими ссылки на l-значение, но неприменима для вызова функций с параметрами ссылок на r-значение. Например:
void g(int &&i, int& j) {
cout << i << " " << j << endl;
}
Если попытаться вызывать функцию g()
через функцию flip()
, то для параметра ссылки на r-значение функции g()
будет передан параметр t2
. Даже если функции flip()
было передано r-значение, функции g()
будет передан параметр, носящий в функции flip()
имя t2
:
flip2(g, i, 42); //
Параметр функции, как и любая другая переменная, является выражением l-значения (см. раздел 13.6.1). В результате вызов функции g()
в функции flip()
передает l-значение параметру ссылки на r-значение функции g()
.
std::forward()
для сохранения информации типа в вызовеflip()
параметры способом, сохраняющим типы первоначальных аргументов, можно использовать новую библиотечную функцию forward()
. Как и функция move()
, функция forward()
определяется в заголовке utility
. В отличие от функции move()
, функцию forward()
следует вызывать с явным аргументом шаблона (см. раздел 16.2.2). Для этого явного аргумента типа функция forward()
возвращает ссылку на r-значение. Таким образом, типом возвращаемого значения функции forward
будет Т&&
.
Обычно функцию forward()
используют для передачи параметра функции, который определен как ссылка на r-значение, параметру типа шаблона. Благодаря сворачиванию ссылок для типа возвращаемого значения функция forward()
сохраняет характер (l- или r-значение) переданного ей аргумента:
template
finalFcn(std::forward
}
Здесь Type
используется как тип явного аргумента шаблона функции forward()
(выводимый из arg
). Поскольку arg
— это ссылка на r-значение для параметра типа шаблона, параметр Type
представит всю информацию типа в аргументе, переданном параметру arg
. Если этот аргумент будет r-значением, то параметр Type
будет иметь обычный (не ссылочный) тип и функция forward
возвратит Type&&
. Если аргумент будет l-значением, то (благодаря сворачиванию ссылок) типом параметра Type
будет ссылка на l-значение. В данном случае типом возвращаемого значения будет ссылка на r-значение для типа ссылки на l-значение. Снова благодаря сворачиванию ссылок (на сей раз для типа возвращаемого значения) функция forward
возвратит тип ссылки на l-значение.
Т&&
), функция forward()
сохраняет все подробности типа аргумента.
Перепишем первоначальную функцию, используя на этот раз функцию forward()
:
template