2016-12-14 34 views
16
#include <type_traits> 

template <typename T> 
struct C; 

template<typename T1, typename T2> 
using first = T1; 

template <typename T> 
struct C<first<T, std::enable_if_t<std::is_same<T, int>::value>>> 
{ 
}; 

int main() 
{ 
} 

Результаты компиляции различными компиляторами:Должен ли следующий код компилироваться в соответствии со стандартом C++?

MSVC:

error C2753: 'C': partial specialization cannot match argument list for primary template

НКУ-4,9:

error: partial specialization 'C' does not specialize any template arguments

лязг все версии:

error: class template partial specialization does not specialize any template argument; to define the primary template, remove the template argument list

г см-5 +: успешно компилирует

И я хочу дополнительно отметить, что тривиальное специализация как:

template<typename T> 
struct C<T> 
{ 
}; 

успешно не может быть составлен НКУ. Похоже, он выясняет, что специализация в моем первоначальном примере нетривиальна. Поэтому мой вопрос - это шаблон, подобный этому, явно запрещенный стандартом C++ или нет?

+7

Я начинаю жаль, что я никогда не писал [эту вещь] (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). –

+2

@ T.C. Я был в процессе написания этой дедукции влечет за собой замену аргументов на невыводимые вхождения в «P» (поэтому второй шаблон более специализирован, потому что уникальный синтезированный тип не сможет вызвать частичную специализацию is_same), но я «Не уверен, что это явное в формулировке в любом месте и в какой степени оно действительно применяется. – Columbo

+0

@Columbo Конец [temp.deduct.type]/1 (", который сделает P после подстановки выведенных значений (назовем это выведенным A)"), вроде? Но я думаю, что в лучшем случае неясно, как справиться с этой заменой в любом случае. Что, если признаком является 'template struct is_not_int: true_type {}; template <> struct is_not_int : false_type {}; '? Строгое чтение скажет, что частичная специализация тогда недействительна, но это кажется ... причудливым в лучшем случае. –

ответ

1

Я думаю, вы могли бы упростить свой код - это не имеет никакого отношения к type_traits. Вы получите те же результаты, с следующим:

template <typename T> 
struct C; 

template<typename T> 
using first = T; 

template <typename T> 
struct C<first<T>> // OK only in 5.1 
{ 
}; 

int main() 
{ 
} 

Проверить в онлайн компилятор (компилирует под 5.1, но не с 5.2 или 4.9, так что это, вероятно, ошибка) - https://godbolt.org/g/iVCbdm

Я думаю, что ИНТ GCC 5 они перемещались по функциональности шаблона, и даже можно создать две специализации одного типа. Он будет скомпилирован, пока вы не попытаетесь его использовать.

template <typename T> 
struct C; 

template<typename T1, typename T2> 
using first = T1; 

template<typename T1, typename T2> 
using second = T2; 

template <typename T> 
struct C<first<T, T>> // OK on 5.1+ 
{ 
}; 

template <typename T> 
struct C<second<T, T>> // OK on 5.1+ 
{ 
}; 

int main() 
{ 
    C<first<int, int>> dummy; // error: ambiguous template instantiation for 'struct C<int>' 
} 

https://godbolt.org/g/6oNGDP

Это может быть как-то связано с дополнительной поддержки для C++ 14 переменных шаблонов. https://isocpp.org/files/papers/N3651.pdf

+0

Кажется, что в 5.1 ваш пример работает, но начиная с 5.2 он не может скомпилироваться даже с одним тривиальным псевдонимом шаблона с «error: partial specialization» struct C «не специализируется на аргументах шаблона». Так что gcc развивается в этом отношении, кажется :) – Predelnik

+0

Скорее всего, это ошибка, введенная с __N3651__ до __GCC 5.1__ и частично исправленная в __5.2__. Частично, потому что второй пример будет скомпилирован на __5.2 до 6.2__ (не тестировался дальше). Обратите внимание, что я отредактировал второй пример, добавив второй тип фиктивного шаблона. Я не уверен, действительно ли это ошибка или функция. По моему мнению, он будет специализировать __struct C__ для __int__ дважды. В случае функции это будет проблемой, поскольку вы можете специализировать функцию в отдельном блоке компиляции и связываться с ней - вы получите множественное определение. В случае класса/структуры это немного сложнее. –

+0

В любом случае, если компилятор позволяет определить несколько специализаций для класса шаблонов, как они могут решить, какой из них один? Мы не можем. –

4

Ключевым пунктом является [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 показывает, какой уровень детализации требуется для «правильного» определения того, является ли домен шаблона надлежащим подмножеством другого. Практически и разумно реализовать частичный заказ, чтобы быть таким сложным. Тем не менее, раздел, посвященный сноскам, просто показывает, что этот вопрос не обязательно указан ниже, но является дефектным.

Смежные вопросы