gcc и ключевое слово класса - PullRequest
8 голосов
/ 18 апреля 2011

Я знаю, что ключевые слова typename и class являются взаимозаменяемыми в аргументах шаблона, но я думал, что только typename разрешено для спецификации вложенных классов.

Однажды я случайно написал неправильно "class" вместо "typename" для вложенного класса. И я обнаружил, что gcc также принимает class, поэтому вы можете написать что-то вроде:

class std::vector<T>::iterator it;
instead of
typename std::vector<T>::iterator it;

в вашем шаблоне.

Это ошибка gcc или стандарт действительно допускает такой синтаксис?

ОБНОВЛЕНИЕ: пример кода:

template <typename T>
void test()
{
     class std::vector<T>::iterator it;
}

Ответы [ 6 ]

2 голосов
/ 20 апреля 2011

class a::b - подробный спецификатор типа. Поиск имени для разработанного спецификатора типа игнорирует не типовые имена. Поэтому, если вы анализируете шаблон, вы можете предположить две вещи:

  • Когда мы создаем экземпляр и выполняем поиск по имени для b, либо поиск по имени дает нам тип, либо он выдает ошибку (не найдет никакого имени).

По этой причине в C ++ 0x class a::b не требуется typename (вы нигде не можете поместить его в разработанный спецификатор типа). C ++ 03 не позволяет этого, поэтому GCC, по-видимому, реализует правила C ++ 0x как расширение.

Это не особенно плохо. Каждый реальный компилятор реализует правила, которые разумны и просты для реализации в их версии C ++ 03, от их имени, даже если формально они должны будут отклонить его. Тем не менее, class a::b должен найти имя класса. Если это просто определение типа, то поиск для разработанного спецификатора типа недопустим.

Обратите внимание, что class a::b - это единственный способ игнорировать не типовые имена в поиске (за исключением непонятных случаев, как до :: в квалифицированном имени, которые имеют подобные специальные правила). Например

template<typename T> 
struct A { typename T::type t; } 

struct B { class type { }; int type; };

// invalid, even though GCC accepts that incorrectly
A<B> a;

Если вы компилируете в C ++ 0x и используете class T::type t;, код становится действительным, потому что class T::type игнорирует член данных, но находит вложенный класс.

2 голосов
/ 18 апреля 2011

Раздел 14.6 («Разрешение имен») в ISO 14886: 2003, похоже, определяет, как это должно работать. Пункт 3 гласит:

A квалифицированный идентификатор , который относится к типу и в котором спецификатор вложенного имени зависит от параметра шаблона (14.6.2) должен иметь префикс ключевого слова typename, чтобы указать, что qualid-id обозначает тип, образуя подробный спецификатор типа (7.1.5.3).

Нет упоминания о ключевом слове class. Я думаю, что это ошибка GCC.

1 голос
/ 18 апреля 2011

Конечно, стандарт подтверждает, что следует использовать имя типа.Начиная с 14.6 / 2:

Предполагается, что имя, используемое в объявлении или определении шаблона и зависящее от параметра-шаблона, не называет тип, если только соответствующий поиск имени не найдет имя типа илиимя уточняется по ключевому слову typename.

1 голос
/ 18 апреля 2011

Не удается скомпилировать с Comeau Online (Comeau C / C ++ 4.3.10.1 (6 октября 2008 11:28:09)), поэтому по крайней мере один из двух компиляторов имеет ошибку.

error: typedef "iterator" may not be used in an elaborated
          type specifier
       class std::vector<T>::iterator it;
0 голосов
/ 18 апреля 2011

Ideone показывает приятное сообщение об ошибке, когда зависимое имя не разрешается в классе. Так что ваш пример работает только потому, что iterator действительно class. :)


Редактировать : MSVC не может скомпилироваться, даже если зависимый действительно класс,

ошибка C2242 : имя typedef не может следовать за class / struct / union

Редактировать 2 : Кажется, это ошибка MSVC, так как g ++ и Comeau online компилируются просто отлично, если зависимое имя является классом.

0 голосов
/ 18 апреля 2011

Я все еще теряюсь в том, что случилось. Я бы добавил комментарий, если бы там не было проблем с кодом.

template <typename Foo>
void f()
{
    class Foo::bb x;
}

struct X {
    typedef int bb;
};

int main()
{
    f<X>();
    return 0;
}

не компилируется с gcc с ошибкой во время создания экземпляра. Он скомпилируется, если X :: bb является классом. Комо ведет себя так же.

Отредактируйте, я думаю, что теперь я лучше понимаю.

class Foo::Bar;

является спецификатором разработанного класса. Foo :: Bar рассматривается как квалифицированное имя (3.4.4 / 3). Поскольку это зависит, поиск должен быть сделан на втором этапе, во время реализации. Затем, если он не найден или не является именем класса или перечислимым именем, разработанный спецификатор класса неверен.

TL; DR g ++, похоже, не содержит ошибку.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...