Может ли явная специализация шаблона шаблона объявить что-то еще?

Было бы неплохо, если бы этот код был недействительным. Но это концептуально звучит, и GCC принимает его, хотя Комо не делает:

template< typename > struct t;

template<> struct t< int > {} r; // Bad declarator! Don't pee on the carpet!

( Изменить: приведенные выше компиляции, но r кажется, что не объявлено в любой области, поэтому оно по существу игнорируется.)

Явные специализации заполняют некую область между шаблонами и классами. Тип, объявленный явной специализацией, завершен после его определения. С точки зрения компилятора это не шаблон. Если бы это был параметризованный шаблон, объявление объекта было бы невозможным. Рассмотрим §14/3:

В декларации шаблона, явной специализации или явного экземпляра init-declarator-list в декларации должен содержать не более одного декларатора. Когда такое объявление используется для объявления шаблона класса, декларатор не разрешается.

Что означает "используется для объявления шаблона класса"? Очевидно, что первичный шаблон объявляет шаблон класса. И частичная специализация тоже, согласно §14.5.5/1 (номера FDIS):

Объявление шаблона, в котором имя шаблона класса является идентификатором simple-template, является частичной специализацией шаблона класса, названного в идентификаторе simple-template-id.

Однако, когда дело доходит до явных специализаций, стандарт говорит в терминах объявления, которому предшествует последовательность токенов template<>. Он выглядит как шаблон, и он называет имя шаблона, но, похоже, не объявляет шаблон.

Действительно странно, что §14/3 ограничивает число деклараторов "не более одного". Объявление шаблона функции, явная специализация или экземпляр должны иметь ровно один декларатор. Любое объявление, включающее шаблон класса, должно иметь ровно нулевое значение... кроме явной специализации, которая, кажется, попадает в трещины. Верно, GCC отказывается разрешать

template<> struct t< int > {} r, s; // Offer valid one per specialization.

Я склонен согласиться с интерпретацией GCC, вздор, как это может быть. К сожалению, может быть запретить его способность обнаруживать недостающие точки с запятой. Пожалуйста, пусть число разрешенных деклараторов будет равно нулю!

Ответ 1

Явная специализация и явное создание не объявляют шаблон. Они объявляют идентификатор шаблона, который ссылается на специализацию, которая является классом.

Однако это не подтверждает мой пример. Проблема в том, что все объявленные после template или template<> являются частью явного экземпляра или специализации соответственно. Только определенные типы объектов могут быть специализированными или инстанцированными, а ранее не объявленные имена не являются одним из них.

Рассмотрим эти примеры, делающие бесплатное, но законное использование спецификаторов специфицированных типов (§7.1.5.3):

template< typename T > struct s;
template< typename T > s< int > *f() {}

template<> struct u *f< char >(); // struct u is declared
u *p = 0; // see, we can use its name now.
template<> struct s< int > *f< int >(); // s<int> declared but not specialized
template struct s< int > *f< long >(); // s<int> declared but not instantiated

Насколько я могу судить, Стандарт нечетко указывает, какое объявленное имя является специализированным. Язык слабо означает, что каждое такое выражение относится только к одному шаблону: §14.7.2/2

Если явное инстанцирование для класса, функция или спецификация шаблона члена...

и §14.7.3/2

В пространстве имен, в котором шаблон является членом, должна быть объявлена ​​явная специализация...

Единственный способ разрешить это - игнорировать объявление типа, если декларатор также указывает юридический экземпляр/специализацию.

Как можно догадаться, примеры в вопросе определяют незаконные специализации в деклараторе, а затем ожидают, что компилятор откатится и специализируется на типе. Учитывая явные списки того, какие специализации и декларации разрешены делать в §14.7.2/1 и §14.7.3/1, представляется более разумным жаловаться на template<> struct t< int > {} r;, что r не является шаблоном функции, функцией-членом шаблон, статический элемент данных шаблона класса и т.д.

Ответ 2

Несколько точек: во-первых, явные специализации не находятся в состоянии область между шаблонами и классами; явная специализация класс, период. Единственное отношение имеет с шаблонами (кроме смешное имя) заключается в том, что он будет использоваться вместо создания шаблона если шаблон должен быть создан по типу специализации.

Во-вторых, если есть проблема с параграфом в §14/3, то вы цитирует, что он включает в себя явное инстанцирование; явный экземпляр - это определение класса, а если

struct S {} s, *p;

является законным,

template<> struct T<int> {} s, *p;

тоже должно быть. (Я бы возражал против того, чтобы позволить, но этот поезд уже покинул станцию, а так как C разрешает первое, мы застряли с ним.)

В противном случае утверждение в §14/3 немного не имеет значения. Функция шаблон должен иметь ровно один декларатор и шаблон класса точно нуль; нет необходимости пытаться занять их как в "не более чем одном", абракадабра. (Если бы я разрабатывал язык с нуля, я бы не разрешить любой декларатор в объявлении, определяющем класс или перечисление тип. Но опять же, это слишком поздно для этого.)

И я согласен, что это беспокоит:

template<> struct T<int> {};    //  Requires a ';'
template<> void f<int>() {}     //  ';' forbidden

(По крайней мере, С++ 11 разрешает точку с запятой после определения функции.)