template <int x, int y> struct S {}; // [1]
template <int w, int z> struct S {}; // [2] <-- ill-formed
Это запрещено стандартным , общим C ++ материалом.
Имена обозначают объекты (или метки, но давайте проигнорируем это) . Имена взяты из объявлений . Определение является подмножеством объявления , поэтому оно также вводит имена.
Правило одного определения гласит:
НетЕдиница перевода должна содержать более одного определения любой переменной, функции, типа класса, типа перечисления или шаблона.
В операторе [1]
создается шаблонная сущность. Эта сущность обозначается именем S
.
В операторе [2]
создается шаблонная сущность. Эта сущность обозначается именем S
.
Эти два имени являются одинаковыми . Если имя обозначает объект (как ранее установлено), то то же имя в соответствующей области действия обозначает тот же объект. Таким образом, оба заявления объявляют одну и ту же сущность.
Что запрещено ODR.
template <> struct S<6+1,4+3> {}; // [5]
template <> struct S<2+5,8-1> {}; // [6] <-- ill-formed
Грамматически говоря, S<6+1,4+3>
- это simple-шаблон-ID . Теперь можно подумать, что это превращает это в определение частичной специализации шаблона класса . Но это не так, потому что параметр шаблона пуст, что делает это явной специализацией . Несмотря на это, S<6+1,4+3>
все еще является названием этого определения. И явные определения специализации по-прежнему являются определениями.
Итак, вопрос в том, является ли S<6+1,4+3>
тем же именем, что и S<2+5,8-1>
?
Вот выдержка из правил о том, как мы говорим, что то же имя :
они идентификаторы шаблона , которые ссылаются на один и тот же класс, функцию или переменную ([temp.type])
A simple-template-id является грамматически подмножеством template-id . Итак, эти два simple-template-id s "относятся к одному и тому же классу, функции или переменной"?
Это определяется сложным набором правил ,В частности, обратите внимание:
их соответствующие нетипизированные аргументы шаблона целочисленного или перечислимого типа имеют одинаковые значения
6+1
и 2+5
- это одно и то же значение. 4+3
и 8-1
- это одно и то же значение. Следовательно, эти два simple-template-id s имеют одинаковое имя и, следовательно, одну и ту же сущность. И поскольку эти два определения определяют одну и ту же сущность, которая нарушает ODR, как указывалось ранее.
Частичная специализация шаблонов - это другое дело.
template <int x> struct S<x,5+2> {}; // [3]
template <int w> struct S<w,3+4> {}; // [4] <-- ill-formed
Предыдущая логика, окружающая simple-template-id
работает ... в точку. Частичные специализации используют simple-template-id s для своих имен, поэтому вводимые ими сущности используют одни и те же правила для проверки эквивалентности.
Однако эти правила на самом деле не говорят, что здесь происходит,x
и w
не являются значениями;они являются параметрами шаблона. Их действительная ценность может быть известна только тогда, когда выбраны их специализации. А другие правил эквивалентности на самом деле не говорят о том, что происходит, когда аргумент шаблона является параметром шаблона.
Так что технически , может показаться, что это не тактакое же определение. Однако ...
В соответствии с правилами , как выбирается специализация шаблона , операторы [3]
и [4]
вводят частичные специализации, которые полностью неоднозначно . Видите, правила выбора между специализациями включают в себя сложный набор вещей, включая преобразование ваших частичных специализаций в серию объявлений функций и использование правил перегрузки шаблонных функций для их сортировки. ,Поэтому я не цитирую это.
То, как все сводится к вашим двум типам, заключается в том, что любой S<value>
, который вы пытаетесь создать, будет неоднозначным. Нет никакого способа обнаружить [3]
из [4]
, поэтому любая попытка использовать любой из них будет плохо сформирована.
Таким образом, ваш компилятор в основном прыгает, понимая, что вы не можете использовать ни одну из специализаций, и говорит вам, что ваш код не работает.
Возможно, что-то в спецификации было пропущено, что фактическичто имена одинаковы, поскольку они , очевидно, одно и то же имя. Но я не нашел его.