Ключевым пунктом является [temp.class.spec]/(8.2), что требует, чтобы частичная специализация была более специализированной, чем основной шаблон. То, что Clang на самом деле жалуется, - это список аргументов, идентичный первому шаблону: это было удалено с [temp.class.spec]/(8.3) на issue 2033 (в котором указано, что требование было избыточным) довольно недавно, поэтому еще не реализовано в Clang. Однако, по-видимому, он был реализован в GCC, учитывая, что он принимает ваш фрагмент; он даже compiles the following, возможно, по той же причине он компилирует код (он также работает только с версии 5 и выше):
template <typename T>
void f(C<T>) {}
template <typename T>
void f(C<first<T, std::enable_if_t<std::is_same<T, int>::value>>>) {}
Т.е. он признает, что декларации отличаются друг от друга, поэтому должно было выполнить некоторое решение вопроса 1980. Он не обнаружил, что вторая перегрузка более специализирована (см. Ссылку Wandbox), однако, которая несовместима, поскольку она должна была диагностировать ваш код в соответствии с вышеупомянутым ограничением в (8.2).
Возможно, текущая формулировка делает частичный порядок заказа вашего примера по желанию & dagger;: [temp.deduct.type]/1 упоминает, что в вычете из типов,
Template arguments can be deduced in several different contexts, but in each case a type that is specified in terms of template parameters (call it P
) is compared with an actual type (call it A
), and an attempt is made to find template argument values […] that will make P
, after substitution of the deduced values (call it the deduced A
), compatible with A
.
Теперь через [temp.alias]/3, это будет означать, что во время стадии частичного упорядочения, в котором шаблон функции частичной специализации является шаблоном параметра, подмена в is_same
дает ложной (с общие реализации библиотеки просто используют частичную специализацию, которая должна завершиться неудачей), а enable_if
терпит неудачу. & Кинжал; Но эта семантика не является удовлетворительной в общем случае, потому что мы могли бы построить условие, которое обычно преуспевает, поэтому встречается уникальный синтезированный тип, и вывод выполняется в обоих направлениях.
Предположительно, самым простым и надежным решением является игнорирование отброшенных аргументов при частичном упорядочении (что делает ваш пример плохо сформированным). Можно также ориентироваться к поведению реализаций в данном случае (аналогичной выпуск 1157):
template <typename...> struct C {};
template <typename T>
void f(C<T, int>) = delete;
template <typename T>
void f(C<T, std::enable_if_t<sizeof(T) == sizeof(int), int>>) {}
int main() {f<int>({});}
Оба Clang и GCC диагностировать это как вызов удаленных функций, т.е. соглашаются, что первая перегрузка является более специализированной, чем другие , Критическое свойство # 2, по-видимому, состоит в том, что второй аргумент шаблона зависит еще от T
появляется только в невыводимых контекстах (если мы изменим int
на T
в # 1, ничего не изменится). Таким образом, мы могли бы использовать существование отброшенных (и зависимых?) Аргументов шаблона в качестве тай-брейкеров: таким образом нам не нужно рассуждать о природе синтезированных ценностей, что является статус-кво, а также получить разумное поведение в вашем случае , который будет хорошо сформирован.
& dagger; @ T.C. что шаблоны, сгенерированные через [temp.class.order], в настоящее время будут интерпретироваться как один многократно объявленный объект —, см. вопрос 1980. В этом случае это не имеет прямого отношения к стандартным, поскольку в формулировке никогда не упоминается, что эти шаблоны функций объявлены, не говоря уже о той же программе; он просто указывает их, а затем возвращается к процедуре для шаблонов функций.
& Dagger; Не совсем понятно, какие глубинные реализации необходимы для выполнения этого анализа. Выпуск 1157 показывает, какой уровень детализации требуется для «правильного» определения того, является ли домен шаблона надлежащим подмножеством другого. Практически и разумно реализовать частичный заказ, чтобы быть таким сложным. Тем не менее, раздел, посвященный сноскам, просто показывает, что этот вопрос не обязательно указан ниже, но является дефектным.
Я начинаю жаль, что я никогда не писал [эту вещь] (http://stackoverflow.com/a/31213703/2756719). Возникает вопрос, является ли попытка частичной специализации более специализированной, чем первичный шаблон (https://timsong-cpp.github.io/cppwp/temp.class.spec#8.2), которая определяется путем их сравнения [после перезаписи к двум шаблонам функций] (https://timsong-cpp.github.io/cppwp/temp.class.order). К сожалению, [даже не ясно, после объявления двух объявлений шаблонов функций объявляет один и тот же шаблон функции или нет) (https://wg21.link/cwg1980). –
@ T.C. Я был в процессе написания этой дедукции влечет за собой замену аргументов на невыводимые вхождения в «P» (поэтому второй шаблон более специализирован, потому что уникальный синтезированный тип не сможет вызвать частичную специализацию is_same), но я «Не уверен, что это явное в формулировке в любом месте и в какой степени оно действительно применяется. – Columbo
@Columbo Конец [temp.deduct.type]/1 (", который сделает P после подстановки выведенных значений (назовем это выведенным A)"), вроде? Но я думаю, что в лучшем случае неясно, как справиться с этой заменой в любом случае. Что, если признаком является 'template struct is_not_int: true_type {}; template <> struct is_not_int : false_type {}; '? Строгое чтение скажет, что частичная специализация тогда недействительна, но это кажется ... причудливым в лучшем случае. –