Ответ 1
Почему GCC и Clang считают, что они правы
K
, который является введенным именем класса, имеет двойную природу в области K<int>
. Вы можете использовать его без аргументов шаблона. Тогда это относится к K<int>
(к его собственному типу).
За ним может также следовать список аргументов шаблона. ИМО разумно сказать, что вам нужно префикс его с помощью template
из-за неопределенности парсера с последующим <
. Затем он ссылается на указанный тип, определяемый аргументами шаблона.
Поэтому его можно рассматривать как шаблон-член и как вложенный тип, в зависимости от того, следует ли ему список аргументов шаблона. Конечно, K
на самом деле не является шаблоном-членом. Тем не менее, двойственная природа имени введенного класса кажется мне еще более взломанной.
В стандарте есть пример, который читается следующим образом:
template <class T> struct Base { };
template <class T> struct Derived: Base<int>, Base<char> {
typename Derived::Base b; // error: ambiguous
typename Derived::Base<double> d; // OK
};
Можно с уверенностью сказать, что вы намерены отказаться от template
. В Стандарте говорится
Чтобы имя шаблона было явно определено аргументами шаблона, имя должно быть известно, ссылаясь на шаблон.
Я не вижу, как это не относится к T::K<T>
. Если T
является зависимым типом, вы можете просто откинуться назад, потому что не можете знать, что означает K
при его синтаксическом анализе, поэтому, чтобы понять смысл кода, вы должны просто префикс его с помощью template
. Обратите внимание, что у n3225 тоже есть этот пример, но он не является дефектом: вы можете официально оставить template
, если вы просматриваете собственную область шаблона в С++ 0x (она называется "текущая инстанция" ).
Итак, до сих пор Clang и GCC прекрасны.
Почему Comeau прав
Чтобы сделать его еще более сложным, нам придется рассмотреть конструкторы K<int>
. Существует конструктор по умолчанию, и конструктор копирования неявно объявлен. Имя K<int>::K
будет ссылаться на конструктор K<int>
, если только используемый поиск имени не будет игнорировать имена функций (конструкторов). Будет ли typename T::K
игнорировать имена функций? 3.4.4/3 говорит о спецификаторах специфицированных типов, которые typename ...
являются одним из:
Если имя является квалифицированным идентификатором, имя просматривается в соответствии с его квалификацией, как описано в разделе 3.4.3, но игнорирует любые объявленные имена не-типа.
Однако typename ...
использует различный поиск. 14.6/4 говорит
Обычный квалифицированный поиск имени (3.4.3) используется для поиска идентификатора с идентификатором даже в присутствии typename.
Обычный квалифицированный поиск 3.4.3 не будет игнорировать имена не-типа, как показано в примере, прилагаемом к 14.6/4. Итак, мы найдем конструктор (ы), как указано в 3.4.3.1/1a (дополнительный твист, который это происходит только тогда, когда не-типы не игнорируются, был добавлен в более поздний отчет о дефектах, который реализуются всеми популярными компиляторами С++ 03 хотя):
Если спецификатор вложенного имени назначает класс C, а имя, указанное после вложенного имени-спецификатора, при поиске на C - это имя впрыскиваемого класса C (пункт 9), имя вместо этого рассматривается имя конструктора класса C. Такое имя конструктора должно использоваться только в id-идентификаторе конструктора, которое появляется за пределами определения класса.
Итак, в конце концов, я думаю, что goaw правильно диагностирует это, потому что вы пытаетесь поместить список аргументов шаблона в не-шаблон, а также нарушить требование, указанное в последней части (вы используете это имя в другом месте).
Позвольте изменить его, обратившись к введенному имени производным классом, поэтому не происходит преобразование имени конструктора, и вы действительно получаете доступ к типу, чтобы вы действительно могли добавлять аргументы шаблона:
// just replace struct X with this:
template<typename T>
struct X
{
struct Derived : T { };
typename Derived::template K<T> *p;
};
Теперь все компилируется с помощью goau! Заметьте, что я уже сделал отчет о проблеме, чтобы поговорить об этой точной вещи. См. Неверное разрешение имени конструктора. BTW, если вы объявляете конструктор по умолчанию в K
, вы можете увидеть, что goau дает лучшее сообщение об ошибке, если вы используете T::K<int>
"ComeauTest.c", line 13: error: overloaded function "N::K<T>::K [with T=int]" is
not a template
typename T::template K<T> *p;