Переключение в чрезвычайно-педантичный режим на какое-то время, да, я думаю, что вы пропускаете что-то в стандарте, и нет, это не должно иметь никакого значения в этом случае.
Все стандартные ссылки на N4527, текущий рабочий чертеж.
[14.5.5.2p1] говорит:
Для двух шаблонных класса частичных специализаций, первый более специализированный чем второй, если, учитывая следующее переписывание двух шаблонов функций, то первый шаблон функции является более специализированным , чем второй в соответствии с правилами заказа для шаблонов функций (14.5.6.2):
- первая функция TEM плита имеет те же параметры шаблона, что и первая частичная специализация, и имеет один параметр функции, тип - это спецификация шаблона класса с аргументами шаблона первой частичной специализации, а
- второй шаблон функции имеет те же параметры шаблона, что и вторая частичная специализация и имеет один параметр функции , тип которого является специализацией шаблона класса с шаблоном аргументов второй частичной специализации.
Переход к [14.5.6.2p1]:
[...] частичное упорядочение перегруженных деклараций шаблона функции используется в следующих контекстах, чтобы выбрать шаблон функции для , который относится к специализации по шаблону:
- во время разрешения перегрузки для вызова шаблона функции (13.3.3);
- при выборе адреса функции шаблона;
- при удалении оператора размещения, который является специальным шаблоном, выбирается так, чтобы соответствовать оператору размещения new (3.7.4.2, 5.3.4);
- , когда декларация функции друга (14.5.4), явное инстанцирование (14.7.2) или явная специализация (14.7.3) ссылаются на на специализацию шаблона функции.
Не упоминается частичный заказ специализации шаблонов классов. Тем не менее, [14.8.2.4p3] говорит:
Типы, используемые для определения упорядочения зависят от контекста в который делается частичное упорядочение:
- В контексте вызова функции, используемыми типами являются те типы параметров функции, для которых вызов функции имеет аргументы.
- В контексте вызова функции преобразования используются обратные типы шаблонов функции преобразования.
- В других контекстах (14.5.6.2) используется тип функции шаблона функции.
Несмотря на то, что ссылается на [14.5.6.2], он сказал "другие контексты". Я могу только заключить, что при применении алгоритма частичного упорядочения к шаблонам функций, сгенерированным в соответствии с правилами в [14.5.5.2], используется тип функции шаблона функции, а не список типов параметров, как это было бы для функции вызов.
Итак, выбор частичной специализации t
в вашем первом фрагменте будет эквивалентен не случаю, связанному с вызовом функции, а тому, который принимает адрес шаблона функции (например), который также подпадает под «другие контексты»:
#include <iostream>
template<typename> struct s { typedef void v, w; };
template<typename, typename = void> struct t { };
template<typename C> void f(t<C, typename C::v>) { std::cout << "t<C, C::v>\n"; }
template<typename C> void f(t<s<C>, typename s<C>::w>) { std::cout << "t<s<C>, s<C>::w>\n"; }
int main()
{
using pft = void (*)(t<s<int>>);
pft p = f;
p(t<s<int>>());
}
Излишне говорить (Так как мы все еще в очень-педантичного режиме, я переписал шаблоны функций так же, как, например, в [14.5.5.2p2].) , это также компилирует и печатает t<s<C>, s<C>::w>
. Шансы на это по-разному подействовали, но я должен был попробовать. Учитывая, как работает алгоритм, было бы иметь значение, если бы функциональными параметрами были, например, ссылочные типы (запуск специальных правил в [14.8.2.4] в случае вызова функции, но не в других случаях), но такие формы не могут возникать с шаблонами функций, сгенерированными из специализированных шаблонов классов.
Таким образом, весь этот объезд не помог нам один бит, но ... это language-lawyer
вопроса, мы должны были иметь некоторые стандартные кавычки здесь ...
Есть некоторые активные ядра вопросы, связанные с вашим примером:
1157 содержит примечание, что я считаю уместным:
Вывод аргумента шаблона является попыткой сопоставить P
и выведенным A
; однако вычет аргумента шаблона не указан, если P
и выведенные A
несовместимы. Это может произойти в присутствии неконденсированных контекстов . Несмотря на вводном заявления в 14.8.2.4 [temp.deduct.partial] пункт 9, шаблон вывод аргумента может добиться успеха в определении аргумента шаблона для каждого параметра шаблона при производстве выведенной A
, который не совместит с соответствующим P
.
Я не совсем уверен, что это так четко указано; в конце концов, [14.8.2.5p1] говорит:
[...] найти значения аргументов шаблона [...], которые будут делать P после замены выведенных значений [...], совместимых с A .
и [14.8.2.4] ссылки [14.8.2.5] полностью.Тем не менее, довольно ясно, что частичный порядок шаблонов функций не ищет совместимости, когда задействованы невыводимые контексты, и изменение этого приведет к нарушению множества действительных случаев, поэтому я считаю, что это просто отсутствие надлежащей спецификации в стандарте ,
В меньшей степени 1847 имеет отношение к не выводимым контекстам, возникающим в аргументах для специализированных шаблонов. Он ссылается на резолюцию 1391; Я думаю, что есть некоторые проблемы с этой формулировкой - более подробно в this answer.
Для меня все это говорит о том, что ваш пример должен работать.
Как и вы, я был весьма заинтригован тем, что та же противоречивость присутствует в трех различных компиляторов. Я был еще более заинтригован после того, как подтвердил, что MSVC 14 демонстрирует то же самое поведение, что и другие. Итак, когда у меня появилось какое-то время, я подумал, что я бы быстро взглянул на то, что делает Кланг; это оказалось чем-то быстрым, но оно дало некоторые ответы.
Весь код, относящийся к нашему делу, находится в lib/Sema/SemaTemplateDeduction.cpp
.
Ядром алгоритма дедукции является функция DeduceTemplateArgumentsByTypeMatch
; все варианты удержания в конечном итоге называют его, и затем он рекурсивно используется для перехода через структуру составных типов, иногда с помощью сильно перегруженного набора функций, а некоторые flags для настройки алгоритма на основе определенного типа вычета и выполняются части формы типа.
Важным аспектом, касающимся этой функции является то, что она обрабатывает строго вычитание, а не подстановку. Он сравнивает типы типов, выводит значения аргументов шаблона для параметров шаблона, которые появляются в выведенных контекстах, и пропускает невыводимые контексты. Единственная проверка - это проверка того, что выведенные значения аргументов для параметра шаблона согласованы. Я написал еще несколько о том, как Clang делает вывод во время частичного заказа в the answer I mentioned above.
Для частичного упорядочения функциональных шаблонов алгоритм начинается с функции-члена Sema::getMoreSpecializedTemplate
, которая использует флаг типа enum TPOC
для определения контекста, для которого выполняется частичное упорядочение; перечислениями являются TPOC_Call
, TPOC_Conversion
и TPOC_Other
; само за себя. Затем эта функция вызывает isAtLeastAsSpecializedAs
дважды, вперед и назад между двумя шаблонами и сравнивает результаты.
isAtLeastAsSpecializedAs
включает в себя значение флага TPOC
, производит некоторые корректировки на основе этого и в конечном итоге вызывает прямо или косвенно, DeduceTemplateArgumentsByTypeMatch
. Если это возвращает Sema::TDK_Success
, isAtLeastAsSpecializedAs
выполняет только еще одну проверку, чтобы убедиться, что все параметры шаблона, которые используются для частичного упорядочения, имеют значения. Если это тоже хорошо, он возвращает true
.
И это частичный заказ для шаблонов функций. Основываясь на параграфах, приведенных в предыдущем разделе, я ожидал частичного упорядочивания специализированных шаблонов классов, чтобы вызвать Sema::getMoreSpecializedTemplate
с подходящими шаблонами функций и флагом TPOC_Other
, и все, что должно происходить естественным путем оттуда. Если это так, ваш пример должен работать. Сюрприз: это не то, что происходит.
Частичный заказ специализации шаблонов классов начинается с Sema::getMoreSpecializedPartialSpecialization
. Как оптимизация (красный флаг!), он не синтезирует шаблоны функций, а использует DeduceTemplateArgumentsByTypeMatch
, чтобы сделать вывод типа непосредственно непосредственно в специализированных специализированных шаблонах класса как типы P
и A
. Это отлично; в конце концов, это то, что алгоритм для шаблонов функций в конечном итоге все равно сделает.
Однако, если все идет хорошо во время вычета, тогда он вызывает FinishTemplateArgumentDeduction
(перегрузка для специализации шаблона класса), которая выполняет подстановку и другие проверки, включая проверку того, что замещенные аргументы для специализации are equivalent to the original ones. Это было бы хорошо, если бы код проверял, соответствует ли частичная специализация набору аргументов, но не очень хорошо при частичном упорядочении и, насколько я могу судить, вызывает проблему с вашим примером.
Итак, кажется, что Richard Corden's предположение относительно того, что происходит, правильно, но я не совсем уверен, что это было намеренно. Это больше похоже на недосмотр. Как мы закончили работу со всеми компиляторами, которые ведут себя одинаково, остается загадкой.
На мой взгляд, удаление двух вызовов FinishTemplateArgumentDeduction
из Sema::getMoreSpecializedPartialSpecialization
не принесет вреда и восстановит согласованность с алгоритмом частичного упорядочения. Нет необходимости в дополнительной проверке (сделанной isAtLeastAsSpecializedAs
), что все параметры шаблона также имеют значения, так как мы знаем, что все параметры шаблона выводятся из аргументов специализации; если бы они не были, частичная специализация не соответствовала бы, поэтому мы бы не получили частичного порядка в первую очередь. (Независимо от того, разрешены ли такие частичные специализации в первую очередь, вопрос issue 549.Кланг выдает предупреждение для таких случаев, MSVC и GCC выдают ошибку. Во всяком случае, не проблема.)
В качестве примечания стороны, я думаю все это относится и к overload for variable template specializations.
К сожалению, у меня нет среды сборки, созданной для Clang, поэтому я не могу проверить это изменение на данный момент.
'template constexpr int f (t ) {return 0; } 'не эквивалентен основному шаблону. Это неоднозначно с 't ', потому что 'c :: v' совпадает с аргументом по умолчанию' void'. Функция, соответствующая первичной, это 'template constexpr int f (t < c, d >) {return 0; } ', но это не входит в частичную специализацию, поскольку частичные специализации * должны быть более специализированными, чем первичные, в первую очередь. –
Potatoswatter
Извините, но вопросы [language-lawyer] необходимо подкрепить более чем ссылкой на 6-летние ответы SO. Даже если бы SO были авторитетными, с тех пор все могло измениться. Утверждения, например, что алгоритм синтеза частичного упорядочения не создает шаблоны и вместо этого синтезирует новые уникальные типы, может быть правдой, но вы должны привести текст, который будет так или иначе говорить. Разумеется, компилятор, похоже, использует другой алгоритм между функциями и частичными специализациями, что по крайней мере противоречиво. – Potatoswatter
Нет стандартного текста в том или ином виде. Это не указано. И основным шаблоном является 't', а не 't '. –
Barry