Первое исключение относится к дедукции типа для ссылочного параметра на r-значение. Когда l-значение (например, i
Т&&
), компилятор выводит параметр типа шаблона как тип ссылки на l-значение аргумента. Поэтому, когда происходит вызов f3(i)
, компилятор выводит тип Т
как int&
, а не int
.Выведение типа Т
int&
, казалось бы, означает, что параметр функции f3()
будет ссылкой на r-значение типа int&
. Обычно нельзя (непосредственно) определить ссылку на ссылку (см. раздел 2.3.1). Но это можно сделать косвенно, через псевдоним типа (см. раздел 2.5.1) или через параметр типа шаблона.• X& &
X& &&
и X&& &
сворачиваются в тип X&
.• Тип X&& &&
X&&
.Комбинация правил свертывания ссылок и специального правила дедукции типа для ссылочных на r-значения параметров означает, что можно вызвать функцию f3()
f3()
(ссылке на r-значение) передается l-значение, компилятор выведет тип T
как тип ссылки на l-значение:f3(i); //
f3(ci); //
//
Когда параметр T
T&&
сворачивается в тип ссылки на l-значение. Например, результирующий экземпляр для вызова f3(i)
получится примерно таким://
void f3
//
Параметр функции f3()
Т&&
, а T
— это int&
, таким образом, Т&&
будет int& &&
, что сворачивается в int&
. Таким образом, даже при том, что формой параметра функции f3()
будет ссылка на r-значение (т.е. T&&
), этот вызов создаст экземпляр функции f3()
с типом ссылки на l-значение (т.е. int&
):void f3
//
У этих правил есть два важных следствия.
• Параметр функции, являющийся ссылкой на r-значение для параметра типа шаблона (например, Т&&
• Если аргумент будет l-значением, то выведенный тип аргумента шаблона будет типом ссылки на l-значение, и экземпляр параметра функции будет создан как (обычный) параметр ссылки на l-значение (Т&
Стоит также обратить внимание на то, что параметру функции Т&&
Т&&
T&
).У того факта, что параметр шаблона может быть выведен как ссылочный тип, имеются удивительные последствия для кода в шаблоне:
template
T t = val; //
t = fcn(t); //
if (val == t) { /* ... */ } //
}
Когда вызов функции f3()
42
, T
имеет тип int
. В данном случае локальная переменная t
имеет тип int
и инициализируется при копировании значения параметра val
. При присвоении переменной t параметр val
остается неизменным.