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