Перегрузка шаблона функции со свертыванием ссылок - PullRequest
0 голосов
/ 06 ноября 2018

В дополнение к этому вопросу, Ссылка на значение l удержания шаблона функции и универсальная ссылка , у меня есть еще один вопрос. Обратите внимание, что вам не нужно читать этот пост, чтобы понять это, но это может быть полезно. Кстати, этот пост не показывает идеального решения проблемы, в которую я верю, но это не главное.

template <typename Buf>
void copy (
    Buf&& input_buffer,
    Buf& output_buffer)
{} // 1)

template <typename Buf>
void copy (
    Buf& input_buffer, 
    Buf& output_buffer)
{} // 2)

...

int i = 4;
int j = 6;

copy<int&>(i, j); // copy taking two l-values; calls second instance.

Я ожидаю, что если бы я позвонил copy<int&>(i,j), был бы вызван первый экземпляр copy. Тип Buf указан, поэтому не требует вывода. Reference collision rules приводит к тому, что input_buffer и output_buffer являются ссылками с l-значением. Первый экземпляр - первая действительная функция.

Это не специализация шаблонов, поэтому меня также удивляет, что компилятор фактически выбирает вместо выдачи ошибки.

Второй экземпляр copy может показаться более определенным, но если Buf равен int&, то первый экземпляр также ожидает две ссылки на l-значение.

Итак, вопрос в том, почему предпочтительнее второй экземпляр? Я ожидаю, что компилятор сначала создаст функцию, подставив аргумент шаблона. Затем оказывается, что Buf&& input_buffer в первом экземпляре равно Buf&.

Не стесняйтесь спрашивать, если мне нужно уточнить.

1 Ответ

0 голосов
/ 06 ноября 2018

Оба шаблона (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.

...