Вообще говоря, для успешного дедукции аргумент должен иметь тот же общий вид, что и параметр. Есть некоторые исключения, из которых T &&
может быть выведено из U &
(путем выбора T = U &
), но такое исключение не было указано для этого случая.
14.8.2.5 Выведение аргументы шаблона из типа [temp.deduct.type]
8 Тип аргумент шаблона T
, шаблон аргумент шаблона TT
или шаблон, не аргумент типа i
может быть выводится, если P
и A
иметь один из следующих форм:
[...]
T&
T&&
[...]
Это не совсем ясно, но это требует P
(параметр) и A
(аргумент), чтобы оба имеют ту же форму. Им необходимо, чтобы оба были из формы T&
или обеих форм T&&
.Исключения, обстоятельство которых T &&
может быть выведен из U &
, сделано путем изменения T &&
в простой T
до совпадения происходит в ограниченных обстоятельствах:
10 Точно так же, если P
имеет форму, которая содержит (T)
, то каждый тип параметра Pi
соответствующего параметра -типа-списка из P
сравнивается с соответствующим типом параметра Ai
соответствующего параметра -типа-списка из A
. Если P
и A
являются типами функций, которые возникли в результате вычитания при принятии адреса шаблона функции (14.8.2.2) или при выводе аргументов шаблона из объявления функции (14.8.2.6) и Pi
и ии Ai
являются параметрами верхнего уровня параметра тип-лист из P
и A
, соответственно, Pi
регулируется, если он является ссылкой на Rvalue к CV-неквалифицированным параметру шаблона и Ai
является именующей ссылкой, в этом случае типа Pi
изменяется на шаблоне тип параметра (т. е. T&&
изменяется на T
). [...]
и
14.8.2.1 Выведение аргументы шаблона из вызова функции [temp.deduct.call]
3 [...] Если P
является rvalue ссылается на cv-неквалифицированный параметр шаблона, и аргумент является lvalue, вместо A
используется тип «lvalue reference to A
» для вывода типа. [...]
, но подобное исключение не применяется к вашему сценарию.
Это тот же самый принцип, что делает
template <typename T> struct S { };
template <typename T> void f(S<const T>) { }
int main() { f(S<void()>()); }
недействителен: const T
не может быть выведено из void()
, хотя T = void()
даст именно тот результат, и вызов f<void()>
удастся.
удален ответ Wintermute показал, что вы можете использовать
template <typename... Types> // vv-- change here
void print_tuple(const std::tuple<Types...>& value)
вместо: это позволяет Types
выводиться в виде Lvalue ссылок, как ссылки RValue, или как не-ссылок, в зависимости от типа value
.
Вам даже не нужно фиксировать кортеж. Почему не просто 'template void print_tuple (Tuple && value)'? –
0x499602D2
Поскольку было не ясно, по крайней мере, два человека: ваше определение 'print_tuple' * позволяет * разрешить вызов, если аргументы шаблона явно указаны как' print_tuple '. Это хороший вопрос, почему компилятор выводит первые «Типы» по-разному. –
hvd
@hvd Это может быть потому, что строковый литерал является lvalue и отправляется как таковой. – 0x499602D2