Ответ 1
В обоих вызовах функций то, что вы передаете функции, является int &
(в смысле: "lvalue типа int
" ). Итак, с объявлением inc1
, компилятор должен вывести T
таким образом, чтобы T &&
соответствовал предоставленному аргументу, т.е. int &
. Единственный способ сделать это - предположить, что T
есть int &
, потому что тогда T &&
есть int & &&
, что эквивалентно int &
. Таким образом, T
становится int &
, а локальный y
объявляется как таковой.
С другой стороны, в inc2
компилятор должен выводить T
таким образом, чтобы T &
соответствовал указанному типу аргументов, который по-прежнему int &
. Это проще всего сделать, если предположить, что T
- это просто int
, так что вы получите для типа локального y
тогда.
Ответ на несколько комментариев (которые тем временем были удалены): Если у вас есть функция с предопределенным типом аргументов, например
inc3(int x) { /*...*/ }
тогда, когда вы вызываете это, например, как inc3(a)
, компилятор применит любое неявное преобразование, необходимое для аргумента, чтобы он соответствовал. В случае inc3(a)
это означает преобразование a
из int &
(в смысле lvalue) в int
(в смысле rvalue) – который называется преобразованием lvalue-to-rvalue и действительным неявным преобразованием. Это в основном сводится к преобразованию переменной a
в значение, которое оно представляет в это время.
Но когда вы объявляете шаблон, например inc1
и inc2
, из вопроса, а аргумент функции определяется с помощью параметра шаблона, тогда компилятор не будет или не только попытается применить неявное конверсии в аргумент, чтобы он соответствовал. Вместо этого он выберет параметр типа аргумента T
, чтобы он соответствовал указанному типу аргументов. Правила для этого сложны, но в случае объявления аргумента типа T &&
они работают, как описано выше. (В случае объявления аргумента pure T
аргумент lvalue все равно будет проходить преобразование lvalue-to-rvalue, а T
будет выводиться как int
, а не int &
.)
Вот почему, хотя int &&
является ссылкой на rvalue, T &&
(где T
является параметром шаблона) не обязательно является ссылкой rvalue. Вместо этого, любой результат от установки T
к предоставленному аргументу. Таким образом, выражение T &&
в этом случае было названо универсальной ссылкой (в отличие от ссылки lvalue или rvalue) – это ссылка, которая при необходимости становится либо lvalue, либо rvalue.