Разница между ‘struct’ и ‘typedef struct’ в C ++?

В C ++ существует ли разница между:

struct Foo { ... }; 

а также

 typedef struct { ... } Foo; 

В C ++ существует только тонкая разница. Это отрыв от C, в котором он имеет значение.

Стандарт C-языка ( C89 §3.1.2.3 , C99 §6.2.3 и C11 §6.2.3 ) требует отдельных пространств имен для разных категорий идентификаторов, включая идентификаторы тегов (для struct / union / enum ) и обычные идентификаторы (для typedef и другие идентификаторы).

Если вы только что сказали:

 struct Foo { ... }; Foo x; 

вы получите ошибку компилятора, потому что Foo определяется только в пространстве имен тегов.

Вы должны объявить это как:

 struct Foo x; 

Каждый раз, когда вы хотите обратиться к Foo , вам всегда нужно называть это struct Foo . Это раздражает быстро, поэтому вы можете добавить typedef :

 struct Foo { ... }; typedef struct Foo Foo; 

Теперь struct Foo (в пространстве имен тегов) и просто Foo (в пространстве имен обычных имен) относятся к одной и той же вещи, и вы можете свободно объявлять объекты типа Foo без ключевого слова struct .


Конструкция:

 typedef struct Foo { ... } Foo; 

это просто аббревиатура для декларации и typedef .


В заключение,

 typedef struct { ... } Foo; 

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


В C ++ все объявления struct / union / enum / class действуют так, как будто они неявно typedef , если имя не скрыто другим объявлением с тем же именем. См . Ответ Майкла Берра за подробностями.

В этой статье DDJ Дэн Сакс объясняет одну небольшую область, где ошибки могут проскальзывать, если вы не набираете свои структуры (и classы!):

Если вы хотите, вы можете себе представить, что C ++ генерирует typedef для каждого имени тега, например

 typedef class string string; 

К сожалению, это не совсем точно. Хотелось бы, чтобы все было так просто, но это не так. C ++ не может генерировать такие typedef для структур, союзов или перечислений без введения несовместимости с C.

Например, предположим, что программа C объявляет как функцию, так и структуру с именем status:

 int status(); struct status; 

Опять же, это может быть плохой практикой, но это C. В этой программе статус (сам по себе) относится к функции; Статус структуры относится к типу.

Если C ++ автоматически генерирует typedefs для тегов, то при компиляции этой программы как C ++ компилятор будет генерировать:

 typedef struct status status; 

К сожалению, это имя типа будет конфликтовать с именем функции, и программа не будет компилироваться. Вот почему C ++ не может просто генерировать typedef для каждого тега.

В C ++ tags действуют так же, как имена typedef, за исключением того, что программа может объявлять объект, функцию или перечислитель с тем же именем и той же областью, что и тег. В этом случае имя объекта, функции или перечислителя скрывает имя тега. Программа может ссылаться на имя тега только с помощью ключевого слова class, struct, union или enum (если необходимо) перед именем тега. Имя типа, состоящее из одного из этих ключевых слов, за которым следует тег, является спецификатором специфицированного типа. Например, статус структуры и месяц enums являются спецификаторами специфицированных типов.

Таким образом, программа C, содержащая оба:

 int status(); struct status; 

ведет себя так же, когда компилируется как C ++. Только статус имени относится к функции. Программа может ссылаться на тип только с использованием статуса структуры специфицированного типа-спецификатора.

Итак, как это может привести к ошибкам в программах? Рассмотрим программу в листинге 1 . Эта программа определяет class foo с конструктором по умолчанию и оператор преобразования, который преобразует объект foo в char const *. Выражение

 p = foo(); 

в main должен построить объект foo и применить оператор преобразования. Последующее выступление

 cout << p << '\n'; 

должен отображать class foo, но это не так. Он отображает функцию foo.

Этот неожиданный результат возникает, потому что программа включает в себя заголовок lib.h, показанный в листинге 2 . Этот заголовок определяет функцию, также называемую foo. Имя функции foo скрывает имя classа foo, поэтому ссылка на foo in main ссылается на функцию, а не на class. main может относиться к classу только с использованием специфицированного спецификатора типа, как в

 p = class foo(); 

Способ избежать такой путаницы во всей программе - добавить следующий typedef для имени classа foo:

 typedef class foo foo; 

непосредственно перед или после определения classа. Этот typedef вызывает конфликт между именем типа foo и именем функции foo (из библиотеки), который вызывает ошибку времени компиляции.

Я не знаю никого, кто действительно пишет эти typedefs, как само собой разумеющееся. Это требует много дисциплины. Поскольку частота ошибок, таких как одна из листинга 1 , вероятно, довольно мала, вы многие никогда не сталкиваетесь с этой проблемой. Но если ошибка в вашем программном обеспечении может привести к телесным повреждениям, тогда вы должны написать typedefs независимо от того, насколько маловероятна ошибка.

Я не могу себе представить, почему кто-нибудь захочет скрыть имя classа с именем функции или объекта в той же области, что и class. Правила сокрытия в C были ошибкой, и их не следует распространять на classы на C ++. Действительно, вы можете исправить ошибку, но она требует дополнительной дисциплины и усилий программирования, которые не должны быть необходимы.

Еще одно важное отличие: typedef s не может быть объявлен вперед. Таким образом, для параметра typedef вы должны #include файл, содержащий typedef , что означает все, что #include s .h также включает в себя этот файл, независимо от того, нужен он ему прямо или нет, и так далее. Это может повлиять на время вашей сборки на более крупные проекты.

Без typedef в некоторых случаях вы можете просто добавить форвардное объявление struct Foo; в верхней части вашего .h файла и только #include определение структуры в ваш .cpp файл.

Есть разница, но тонкая. Посмотрите на это так: struct Foo вводит новый тип. Второй создает псевдоним Foo (а не новый тип) для неназванного типа struct .

7.1.3 Спецификатор typedef

1 […]

Имя, объявленное с помощью спецификатора typedef, становится typedef-name. В пределах своей декларации имя typedef синтаксически эквивалентно ключевому слову и называет тип, связанный с идентификатором, способом, описанным в разделе 8. Таким образом, typedef-name является синонимом другого типа. Имя typedef не вводит новый тип, как это делает объявление classа (9.1) или объявление enum.

8 Если декларация typedef определяет неназванный class (или перечисление), первое имя typedef, объявленное объявлением как этот тип classа (или тип enums), используется для обозначения типа classа (или типа enums) только для целей привязки ( 3,5). [ Пример:

 typedef struct { } *ps, S; // S is the class name for linkage purposes 

Таким образом, typedef всегда используется в качестве заполнителя / синонима для другого типа.

Вы не можете использовать форвардную декларацию с помощью структуры typedef.

Сама структура является анонимным типом, поэтому у вас нет фактического имени для переадресации объявления.

 typedef struct{ int one; int two; }myStruct; 

Передовая декларация вроде этого не работает:

 struct myStruct; //forward declaration fails void blah(myStruct* pStruct); //error C2371: 'myStruct' : redefinition; different basic types 

Важное различие между «typedef struct» и «struct» в C ++ заключается в том, что инициализация inline-члена в «typedef structs» не будет работать.

 // the 'x' in this struct will NOT be initialised to zero typedef struct { int x = 0; } Foo; // the 'x' in this struct WILL be initialised to zero struct Foo { int x = 0; }; 

Struct – создать тип данных. Typedef – установить псевдоним для типа данных.

В C ++ нет никакой разницы, но я верю в C, что позволит вам явно объявлять экземпляры структуры Foo:

 struct Foo bar; 
  • Есть ли эквивалент Java или методология для ключевого слова typedef в C ++?
  • Распространение «typedef» из основанного на производный class для «шаблона»
  • typedef указатель const weirdness
  • Как определить структуру typedef, содержащую указатели на себя?
  • Преобразование цели-c typedef в его эквивалент строки
  • Понимание typedefs для указателей функций в C
  • Что такое перечисление typedef в Objective-C?
  • Непрозрачные C-структуры: как их объявить?
  • Это хорошая идея для указателей typedef?
  • C ++ typedef интерпретация константных указателей
  • Как я могу ввести указатель на функцию с синтаксисом C ++ 11?
  • Давайте будем гением компьютера.