Why Does C++ Disallow Anonymous Structs

Why does C++ disallow anonymous structs?

As others have pointed out anonymous unions are permitted in standard C++, but anonymous structs are not.

The reason for this is that C supports anonymous unions but not anonymous structs*, so C++ supports the former for compatibility but not the latter because it's not needed for compatibility.

Furthermore, there's not much use to anonymous structs in C++. The use you demonstrate, to have a struct containing three floats which can be referred to either by .v[i], or .x, .y, and .z, I believe results in undefined behavior in C++. C++ does not allow you to write to one member of a union, say .v[1], and then read from another member, say .y. Although code that does this is not uncommon it is not actually well defined.

C++'s facilities for user-defined types provide alternative solutions. For example:

struct vector3 {
float v[3];
float &operator[] (int i) { return v[i]; }
float &x() { return v[0]; }
float &y() { return v[1]; }
float &z() { return v[2]; }
};

* C11 apparently adds anonymous structs, so a future revision to C++ may add them.

Are anonymous structs standard? And, really, what *are* they?

All the standard text refers to creating an "unnamed struct":

struct {
int hi;
int bye;
};

Just a nice friendly type, with no accessible name.

In a standard way, it could be instantiated as a member like this:

struct Foo {
struct {
int hi;
int bye;
} bar;
};

int main()
{
Foo f;
f.bar.hi = 3;
}

But an "anonymous struct" is subtly different — it's the combination of an "unnamed struct" and the fact that you magically get members out of it in the parent object:

struct Foo {
struct {
int hi;
int bye;
}; // <--- no member name!
};

int main()
{
Foo f;
f.hi = 3;
}

Converse to intuition, this does not merely create an unnamed struct that's nested witin Foo, but also automatically gives you an "anonymous member" of sorts which makes the members accessible within the parent object.

It is this functionality that is non-standard. GCC does support it, and so does Visual C++. Windows API headers make use of this feature by default, but you can specify that you don't want it by adding #define NONAMELESSUNION before including the Windows header files.

Compare with the standard functionality of "anonymous unions" which do a similar thing:

struct Foo {
union {
int hi;
int bye;
}; // <--- no member name!
};

int main()
{
Foo f;
f.hi = 3;
}

It appears that, though the term "unnamed" refers to the type (i.e. "the class" or "the struct") itself, the term "anonymous" refers instead to the actual instantiated member (using an older meaning of "the struct" that's closer to "an object of some structy type"). This was likely the root of your initial confusion.

Why does C++11 not support anonymous structs, while C11 does?

Little effort has been made to maintain compatibility between C++ and C as the two languages evolve. Notice that variable length stack arrays have been in C since 1999, but weren't included in C++11. While they generally don't introduce things that contradict one another, the C++ committee isn't exactly bending over backwards to make sure that C++11 is compatible with versions of C beyond C89.

Furthermore, this feature would be quite complex in C++, because a struct is nothing more than a class. And an anonymous struct/class should have all of the features of a regular struct/class, yes? Otherwise, what's the point of having it?

What would it mean to construct a nameless struct? How would you define the constructor? Something as simple as:

struct Foo
{
struct
{
size_t &x;
};
};

is simply not possible because the inner struct has no constructor. And there's no way to specify one. A struct cannot construct the members of another struct within it.

For something like this:

struct Foo
{
size_t outer;
struct
{
void SomeFunc();
size_t x;
};
};

What this pointer does SomeFunc get? What would the type of this be, the nameless and unnamed type? How would you even define SomeFunc outside of the struct? The name of SomeFunc can't be Foo::SomeFunc, because SomeFunc lives in an inner scope.

It's just too complex for C++ to deal with. And certainly not worthwhile enough to bother with adding that complexity for.

Why are anonymous structs (non typedef'd) useful in c?

Such un-typedef'd anonymous structures have very limited uses, but not quite zero uses. Basically, they allow you to group some data together in a function or a single source file.

One consquence of that is that any such variables defined at file scope should be static because there isn't a proper declaration available for use in other files. Inside a single function, the variable can be automatic or static as desired; I suppose that if you had struct { … } *peculiar; then you could even manage dynamic allocation. However, if you like playing with fire and double-maintenance nightmares, you can actually have references in other source files to the original variable — you just shouldn't do it! So, it would be possible to have (but please do not do this!):

  • file1.c

    struct { char* name; int age; } alice;
  • file2.c

    extern struct { char* name; int age; } alice;

However, if you need the information shared between source files, create a header and give the structure type a name (either via a tag or a typedef or both).

(Why does this work? Think about what the compiler sees after preprocessing? It sees equivalent information in each TU, and will get it right if the definitions/declarations match. But it is much easier to ensure they match when you have a single header providing them.)

Assuming you aren't indulging in such dubious practices, then you're correct that there's no way to pass such variables around. So you might have a use for a structured variable with file scope but internal linkage that can be accessed by multiple functions in the source file. Such designs are far from unheard of, though they're often a bit dubious.

Note that if you have:

static struct {
char* name;
int age;
} alice;

static struct {
char* name;
int age;
} bob;

then alice and bob have different types (so you can't write bob = alice; or alice = bob;).

So, overall, such variables have limited use, but not zero use. It is seldom a good idea to use such variables, but if there is a good reason (which stands up to careful critical review, preferably by several people), then you can go ahead and use them. But you need to be really sure the cost of a structure tag is not acceptable. (I would add the structure tag at least, if only to document what it represents better.)

Is it legal to define an anonymous struct?

No, it is not allowed. GCC and Clang are right.

Per [dcl.dcl]/3 (7 Declarations) in N3337 (C++11 final draft), a class declaration must introduce at one name to the program. For example, the following are invalid:

enum { };
typedef class { };

(Note: this isn't unique to C++11. In N4140 (C++14 final draft) it is [dcl.dcl]/5 (7 Declarations). In N4659 (C++17 final draft) it is [dcl.dcl]/5 (10 Declarations).)

Why do anonymous structs cause conflicting types

First, these are structure declarations without tags, not anonymous structures. An anonymous structure is a structure without a name, not a structure declaration without a tag. For example:

struct outer
{
struct inner { int x; int y; };
int z;
} s;

In this code, the struct member inside s does not have a member name, so it is anonymous. We have no name that can refer to that structure, and we refer to its members as s.x and s.y, rather than s.something.x and s.something.y.

The reason each declaration of a structure without a tag declares a distinct type is C 2018 6.7.2.3 5 says:

… Each declaration of a structure, union, or enumerated type which does not include a tag declares a distinct type.

A reason for this is sometimes we use structures that have identical contents for different purposes. For example, we might have a structure with two double values that we use for complex numbers (real and imaginary parts) and a structure with two double values that we use for points in a plane (x and y coordinates):

typedef struct { double d[2]; } ComplexNumber;
typedef struct { double d[2]; } Point;

Having the compiler treat these as different types means it can give us warnings about mistakes like passing a Point as an argument where a Complex is expected.

As you have noted, a typedef creates a new name for a type. Then using the typedef name refers to that existing type. It does not “repeat” the declaration of the struct and create a new type.



Related Topics



Leave a reply



Submit