views:

21885

answers:

5

In C++, is there any difference between:

struct Foo { ... };

and

typedef struct { ... } Foo;
+1  A: 

There is no difference in C++, but I believe in C it would allow you to declare instances of the struct Foo without explicitly doing:

struct Foo bar;
kitchen
+78  A: 

In C++, there is no difference. It's a holdover from C, in which it made a difference.

In C, there are two different namespaces of types: a namespace of struct/union/enum tag names and a namespace of typedef names. If you just said

struct Foo { ... };
Foo x;

You would get a compiler error, because Foo is only defined in the tag namespace. You'd have to declare it as

struct Foo x;

Any time you want to refer to a Foo, you'd always have to call it a struct Foo. This gets annoying fast, so you can add a typedef:

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

Now both struct Foo (in the tag namespace) and just plain Foo (in the typedef namespace) both refer to the same thing, and you can freely declare objects of type Foo without the struct keyword. The construct

typedef struct Foo { ... } Foo;

is just an abbreviation for the declaration and typedef. Finally,

typedef struct { ... } Foo;

declares an anonymous structure and creates a typedef for it. Thus, with this construct, it doesn't have a name in the tag namespace, only a name in the typedef namespace. This means it also can't be forward-declared. If you want to make a forward declaration, you have to give it a name in the tag namespace.

In C++, all struct/union/enum/class declarations act like they are implicitly typedef'ed, as long as the name is not hidden by another declaration with the same name. See Michael Burr's answer for the full details.

Adam Rosenfield
While what you say is true, AFAIK, the statement, 'typedef struct { ... } Foo;' creates an alias for an unnamed struct.
dirkgently
Good catch, there's a subtle difference between "typedef struct Foo { ... } Foo;" and "typedef struct { ... } Foo;".
Adam Rosenfield
In C, the struct tags, union tags and enumeration tags share one namespace, rather than (struct and union) using two as claimed above; the namespace referenced for typedef names is indeed separate. That means you can't have both 'union x { ... };' and 'struct x { ... };' in a single scope.
Jonathan Leffler
Aside from the not-quite-typedef thing, another difference between the two pieces of code in the question is that Foo can define a constructor in the first example, but not in the second (since anonymous classes can't define constructors or destructors).
Steve Jessop
@Adam Rosenfield: `"In C++, there is no difference"`, is it because there are no separate tag and typedef namespaces in C++?
Lazer
+2  A: 

There is a difference, but subtle. Look at it this way: struct Foo introduces a new type. The second one creates an alias called Foo (and not a new type) for an unnamed struct type.

7.1.3 The typedef specifier

1 [...]

A name declared with the typedef specifier becomes a typedef-name. Within the scope of its declaration, a typedef-name is syntactically equivalent to a keyword and names the type associated with the identifier in the way described in Clause 8. A typedef-name is thus a synonym for another type. A typedef-name _**does not introduce a new type**_ the way a class declaration (9.1) or enum declaration does.

8 If the typedef declaration defines an unnamed class (or enum), the first typedef-name declared by the declaration to be that class type (or enum type) is used to denote the class type (or enum type) for linkage purposes only (3.5). [ Example:

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

So, a typedef always is used as an placeholder/synonym for another type.

dirkgently
+6  A: 

One more important difference: typedefs cannot be forward declared. So for the typedef option you must #include the file containing the typedef, meaning everything that #includes your .h also includes that file whether it directly needs it or not, and so on. It can definitely impact your build times on larger projects.

Without the typedef, in some cases you can just add a forward declaration of "struct Foo;" at the top of your .h file, and only #include the struct definition in your .cpp file.

Joe
Very good point I hadn't thought about myself!
sbi
+24  A: 

In this DDJ article, Dan Saks explains one small area where bugs can creep through if you do not typedef your structs (and classes!):

If you want, you can imagine that C++ generates a typedef for every tag name, such as

typedef class string string;

Unfortunately, this is not entirely accurate. I wish it were that simple, but it's not. C++ can't generate such typedefs for structs, unions, or enums without introducing incompatibilities with C.

For example, suppose a C program declares both a function and a struct named status:

int status(); struct status;

Again, this may be bad practice, but it is C. In this program, status (by itself) refers to the function; struct status refers to the type.

If C++ did automatically generate typedefs for tags, then when you compiled this program as C++, the compiler would generate:

typedef struct status status;

Unfortunately, this type name would conflict with the function name, and the program would not compile. That's why C++ can't simply generate a typedef for each tag.

In C++, tags act just like typedef names, except that a program can declare an object, function, or enumerator with the same name and the same scope as a tag. In that case, the object, function, or enumerator name hides the tag name. The program can refer to the tag name only by using the keyword class, struct, union, or enum (as appropriate) in front of the tag name. A type name consisting of one of these keywords followed by a tag is an elaborated-type-specifier. For instance, struct status and enum month are elaborated-type-specifiers.

Thus, a C program that contains both:

int status(); struct status;

behaves the same when compiled as C++. The name status alone refers to the function. The program can refer to the type only by using the elaborated-type-specifier struct status.

So how does this allow bugs to creep into programs? Consider the program in Listing 1. This program defines a class foo with a default constructor, and a conversion operator that converts a foo object to char const *. The expression

p = foo();

in main should construct a foo object and apply the conversion operator. The subsequent output statement

cout << p << '\n';

should display class foo, but it doesn't. It displays function foo.

This surprising result occurs because the program includes header lib.h shown in Listing 2. This header defines a function also named foo. The function name foo hides the class name foo, so the reference to foo in main refers to the function, not the class. main can refer to the class only by using an elaborated-type-specifier, as in

p = class foo();

The way to avoid such confusion throughout the program is to add the following typedef for the class name foo:

typedef class foo foo;

immediately before or after the class definition. This typedef causes a conflict between the type name foo and the function name foo (from the library) that will trigger a compile-time error.

I know of no one who actually writes these typedefs as a matter of course. It requires a lot of discipline. Since the incidence of errors such as the one in Listing 1 is probably pretty small, you many never run afoul of this problem. But if an error in your software might cause bodily injury, then you should write the typedefs no matter how unlikely the error.

I can't imagine why anyone would ever want to hide a class name with a function or object name in the same scope as the class. The hiding rules in C were a mistake, and they should not have been extended to classes in C++. Indeed, you can correct the mistake, but it requires extra programming discipline and effort that should not be necessary.

Michael Burr
Nice little unknown (at least for me) Thanks
David Rodríguez - dribeas
In case you try "class foo()" and it fails: In ISO C++, "class foo()" is an illegal construct (the article was written '97, before standardization, it seems). You can put "typedef class foo foo;" into main, then you can say "foo();" (because then, the typedef-name is lexically closer than the function's name). Syntactically, in T(), T must be a simple-type-specifier. elaborated type specifiers are not allowed. Still this is a good answer, of course.
Johannes Schaub - litb
@Michael : `Listing 1` and `Listing 2` links are broken. Have a look.
Prasoon Saurav