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

Является ли имя typedef необязательным в объявлении typedef?

Я был очень удивлен, когда увидел, что следующий код компилируется без ошибок или предупреждений в g++ - 4.2:

typedef enum test { one };

Мое предположение заключалось в том, что если бы вы использовали ключевое слово typedef, для этого потребовался бы дополнительный идентификатор, например:

typedef enum test { one } test;

Как уже упоминалось, g++ - 4.2 принимает его даже без предупреждения. Clang++ 3.0 предупреждает "предупреждение: typedef требует имя", аналогично, Комо предупреждает "предупреждение: декларация требует имени typedef", а g++ - 4.6 сообщает: "предупреждение:" typedef "в этом объявлении игнорировалось".

Я не смог определить, где в стандарте это разрешено, и я нахожу его немного запутанным, что два из компиляторов предупреждают, что это необходимо, не должно быть ошибкой, если требуется имя typedef, но нет?

UPDATE. Я проверил на C с теми же компиляторами. Clang и goau дают тот же результат, gcc дает предупреждение: "предупреждение: бесполезный спецификатор класса хранения в пустой декларации", что кажется еще более запутанным.

UPDATE: я проверил удаление имени перечисления, и результаты будут одинаковыми:

typedef enum { one };

Аналогично названной структуре:

typedef struct named { int x };

Но не с неназванной структурой, и в этом случае код был отклонен в g++ (4.2/4.6) с "error: missing type-name в typedef-declaration", gcc (4.2/4.6) дал предупреждение: "предупреждение: unnamed struct/union, который не определяет экземпляры", clang++ "warning: декларация ничего не объявляет", ошибка при запуске ": объявление требует имени typedef"

4b9b3361

Ответ 1

Это невырожденный синтаксис, который разрешен, но не дает никакой пользы. Большинство современных компиляторов могут быть спровоцированы предупреждением об этом; по умолчанию они не могут. Без имени typedef ключевое слово typedef является излишним; в вашем примере это полностью эквивалентно:

enum test { one };

Другое место, где это может произойти, со структурой:

typedef struct SomeThing { int whatever; };

Это эквивалентно:

struct SomeThing { int whatever; };

Обратите внимание, что typedef официально (или синтаксически) является "спецификатором класса хранения", например static, extern, auto и register.


C Стандарт

В ИСО/МЭК 9899: 1999 (что стандарт С) мы находим:

§6.7 Объявления

Синтаксис

декларация:

Объявление-спецификаторы init-declarator-list opt;

декларирование спецификаторы:

спецификация-спецификатор хранилища opt

спецификатор типа объявления-спецификаторы opt

type-qualifier объявления-спецификаторы opt

function-specifier объявления-спецификаторы opt

INIT-описатель-лист:

init-declarator

init-declarator-list, init-declarator

INIT-описателя:

declarator

declarator = initializer

И (по запросу):

§6.7.1 Спецификаторы класса хранения

Синтаксис

хранения класса спецификатор:

typedef

extern

static

auto

register

Если вы отслеживаете этот синтаксис, существует множество вырожденных возможностей, и то, что вы показали, является одним из многих.


Стандарт С++

Возможно, что С++ имеет разные правила.

В ISO/IEC 14882: 1998 (исходный стандарт С++) мы находим в §7.1.1 "Спецификаторы класса хранения", что С++ не рассматривает typedef как класс хранения; список добавляет mutable и исключает typedef. Итак, грамматическая спецификация typedef в С++ определенно отличается от спецификации C.

§7 Объявления

В объявлениях указывается, как следует интерпретировать имена. Объявления имеют форму

Заявление-сл:

Объявление

декларация объявления-seq

декларация:

объявление блока

определение функции

объявление шаблона

Явное-представление

Явная спецификация

спецификация привязки

namespace-definition

блок-декларация:

простейшая декларация

asm-definition

namespace-alias-definition

using-declaration

с использованием директивы

простое декларирование:

decl-specifier-seq opt init-declarator-list opt;

...

¶5 Если decl-specifier-seq содержит спецификатор typedef, декларация называется объявлением typedef и имя каждого init-declaratorобъявляется как typedef-name, синонимом связанного с ним типа (7.1.3).

§7.1. Спецификаторы [dcl.spec]

Спецификаторами, которые могут использоваться в объявлении, являются

Децл-спецификатора:

спецификатор класса хранения

спецификатор типа

function-specifier

friend

typedef

Децл-спецификатор-сл:

decl-specifier-seq opt

decl-specifier

§7.1.1 Спецификаторы класса хранения [dcl.stc]

хранения класса спецификатор:

auto

register

static

extern

mutable

§7.1.2 Спецификаторы функций [dcl.fct.spec]

функция спецификатор:

inline

virtual

explicit

§7.1.3 Спецификатор typedef [dcl.typedef]

Объявления, содержащие спецификатор decl typedef объявлять идентификаторы, которые могут быть использованы позже для именования фундаментальных (3.9.1) или сложных (3.9.2) типов. Спецификатор typedef не должен использоваться в определении функции (8.4), и он не должен объединяться в спецификаторе decl-seq с любым другим спецификатором, кроме спецификатор типа.

ЬурейеЕ-имя:

идентификатор

...

В заданной области спецификатор typedef может использоваться для переопределения имени любого типа, объявленного в этой области для обозначения того типа, к которому он уже относится. [Пример:

typedef struct s { /* ... */ } s;
typedef int I;
typedef int I;
typedef I I;

-end пример]

§7.1.4 Спецификатор друга [dcl.friend]

Спецификатор друга используется для указания доступа к членам класса; см. 11.4.

§7.1.5 Спецификаторы типа [dcl.type]

тип спецификатор:

спецификатор простого типа

class-specifier

enum-specifier

специфицированный тип-спецификатор

cv-qualifier


Так как в § 7 ¶5 говорится, что имена typedef приходят из init-declarator, а в списке init-declarator помечен 'opt', я думаю, это означает, что имя typedef можно опустить на С++, просто как в C.

Ответ 2

Единственное, что я мог найти, это следующее в стандарте С++ 03 §7.1.3 [dcl.typedef] p1:

ЬурейеЕ-имя:

  • идентификатор

Имя, объявленное с помощью спецификатора typedef, становится именем typedef.

Обратите внимание на отсутствующий opt после идентификатора, который указывает, по крайней мере, на меня, что для имени typedef необходим идентификатор. Странно, что все тестируемые компиляторы (молча) принимают это.


Изменить. После ответа @Jonathan я нашел следующее в том же стандарте, что и выше:

Децл-спецификатора:

  • хранения класса спецификатор
  • тип спецификатор
  • Функция спецификатор
  • friend
  • typedef

Как можно видеть, он предоставляет дополнительный случай для typedef, и список для спецификаторов класса хранения подтверждает это:

хранения класса спецификатор:

  • auto
  • register
  • static
  • extern
  • mutable

Итак, мы так же невежественны, как и раньше, в случае С++.

Ответ 3

По-моему, это похоже на разницу между C и С++. С++ неявно typedefs структурирует и объединяет их теги; поэтому добавление typedef является излишним, но не ошибкой. Я не знаю, работает ли это для перечислений.

Далее следует выяснить, какие определения переменных разрешены после этих объявлений.

enum test etest;
test etest2;
struct named snamed;
named snamed2;