Почему 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
, если вы загляните в собственную область видимости шаблона в C ++ 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 (дополнительный поворот, что это происходит только тогда, когда нетипы не игнорируются, был добавлен в более позднем отчете о дефектах, который все популярныеКомпиляторы C ++ 03 реализуют хотя):
Если спецификатор вложенного имени назначает класс C, а имя, указанное после спецификатора вложенного имени, при поиске в C, являетсяinjected-class-name of C (раздел 9), вместо этого считается, что имя присваивает имя конструктору класса C. Такое имя конструктора должно использоваться только в идентификатор объявления определения конструктора, который появляется вне определения класса.
Итак, в конце концов, я думаю, что Comeau правильно диагностировать это, потому что вы пытаетесь поместить список аргументов шаблона в не шаблон, а также нарушаете требование, указанное в последней части (вы используетеимя в другом месте).
Давайте изменим его, получив доступ к введенному имени с помощью производного класса, так что преобразование имен конструктора не произойдет, и вы действительно получите доступ к типу, так что вы действительно может добавить аргументы шаблона:
// just replace struct X with this:
template<typename T>
struct X
{
struct Derived : T { };
typename Derived::template K<T> *p;
};
Теперь все скомпилируется и с comeau!Обратите внимание, что я уже сделал сообщение о проблеме, чтобы позвонить об этой точной вещи.См. Неверное разрешение имени конструктора .Кстати, если вы объявите конструктор по умолчанию в K
, вы увидите, что comeau выдаст лучшее сообщение об ошибке при использовании 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;