Подтвердить что ты не робот

Еще одна ошибка в g++/Clang? [С++ Templates - это весело]

Посмотрите следующий код (написанный просто для удовольствия)

namespace N
{
   template<typename T>
   struct K
   {

   };
}
template<typename T>
struct X
{
   typename T::template K<T> *p; //should give error 
                                 //N::K<int> has no template member named `K`
};

int main()
{
   X<N::K<int> > l;
}

Код компилируется на g++ (4.5.1) и Clang, тогда как Comeau и Intel С++ дают (похожие) ошибки.

Ошибки, которые я получаю от Комо, следующие:

"ComeauTest.c", line 13: error: class "N::K<int>" has no member "K"
     typename T::template K<T> *p;
                          ^
          detected during instantiation of class "X<T> [with T=N::K<int>]" at
                    line 18

"ComeauTest.c", line 13: error: expected an identifier
     typename T::template K<T> *p;
                           ^
          detected during instantiation of class "X<T> [with T=N::K<int>]" at
                    line 18

Итак, мой вопрос: "Является ли образец кода плохо сформированным?" По словам меня "Да". Означает ли это, что это еще одна ошибка в g++/Clang?

4b9b3361

Ответ 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;