Оба шаблона (1) и (2) создают одну и ту же сигнатуру, поэтому выбор ее зависит от правил частичного упорядочения. Частичное упорядочение работает с объявлениями шаблонов, а не с их специализациями, созданными неявной реализацией, или с их явными специализациями.
В соответствии с правилами частичного упорядочения мы определяем, является ли (1) по крайней мере таким же общим, как (2), и является ли (2) по крайней мере таким же общим, как (1). Если ответы соответственно «да» и «нет», то (2) является более специализированным и выбирается.
Чтобы определить, является ли (1) по крайней мере таким же общим, как (2), мы синтезируем некоторый уникальный тип для Buf
и подставим его в сигнатуру (2), а затем попытаемся вывести аргументы (1) из гипотетическая специализация так сформировалась. Идея состоит в том, что если это работает для уникального типа, то оно также должно работать для любого другого типа.
Действительно, если подставить Buf
= Unique
в (2), мы получим
void copy (Unique& input_buffer, Unique& output_buffer)
В результате Buf
выводится как Unique&
в (1) (это работает из-за правил свертывания ссылок).
Если мы сделаем это наоборот, подставив Buf = Unique
в (1), это приведет к подписи
void copy (Unique&& input_buffer, Unique& output_buffer)
Теперь мы пытаемся вывести Buf
в (2) из этого --- но, конечно, это не работает, потому что (2) может генерировать только те специализации, в которых оба параметра являются ссылками lvalue.