Еще одна ошибка в g ++ / Clang? [C ++ шаблоны это весело] - PullRequest
31 голосов
/ 12 декабря 2010

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

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 C ++ выдают (аналогичные) ошибки.

Ошибки, которые я получаю на Comeau:

"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?

1 Ответ

44 голосов
/ 12 декабря 2010

Почему 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;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...