1

[dcl.typedef]/9:

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 ([basic.link]). [ Note: A typedef declaration involving a lambda-expression does not itself define the associated closure type, and so the closure type is not given a name for linkage purposes. — end note ] [ Example:

typedef struct { } *ps, S;      // S is the class name for linkage purposes
typedef decltype([]{}) C;       // the closure type has no name for linkage purposes

— end example ]

WaldB
  • 1,261
  • 7
  • 14

2 Answers2

1

In the latest working draft, the reference is [dcl.typedef]/4.

The significance of "for linkage purposes" is given by [basic.link]/8:

Two declarations of entities declare the same entity if, considering declarations of unnamed types to introduce their names for linkage purposes, if any ([dcl.typedef], [dcl.enum]), they correspond ([basic.scope.scope]), have the same target scope that is not a function or template parameter scope, and either

  • they appear in the same translation unit, or
  • they both declare names with module linkage and are attached to the same module, or
  • they both declare names with external linkage.

[Note 2: There are other circumstances in which declarations declare the same entity ([dcl.link], [temp.type], [temp.spec.partial]). — end note]

So let's consider the following example:

// S1.h
struct S1 {
    int member;
};

// A.cpp
#include "S1.h"
extern S1 s1;

// B.cpp
#include "S1.h"
S1 s1;

After preprocessing, A.cpp and B.cpp will be as follows:

// A.cpp
struct S1 {
    int member;
};
extern S1 s1;

// B.cpp
struct S1 {
    int member;
};
S1 s1;

Each translation unit contains the definition of a struct named S1. Is A.cpp's S1 the same struct as B.cpp's S1? If the answer is yes, then then B.cpp's definition of the object S1 s1; links with A.cpp's declaration extern S1 s1;. If the answer is no, then the two translation units declare the same variable with different types, and the program is ill-formed (no diagnostic required).

Well, you probably know that we would be in big trouble if B.cpp's definition of s1 were not a definition of A.cpp's s1. It must be the case that both translation units are defining the same type S1.

[basic.link]/8 tells us that this is indeed so. Both declarations of S1 declare it with external linkage. Both have the same target scope---namely, the global scope. And they correspond because they both introduce the same name; [basic.scope.scope]/4:

Two declarations correspond if they (re)introduce the same name, both declare constructors, or both declare destructors, unless [irrelevant exceptions omitted].

So both declarations declare the same type. A.cpp's S1 is the same type as B.cpp's S1. A.cpp's s1 has the same type as B.cpp's s1.

Now let's change the example slightly:

// S2.h
typedef struct {
    int member;
} S2;

// C.cpp
#include "S2.h"
extern S2 s2;

// D.cpp
#include "S2.h"
S2 s2;

After preprocessing:

// C.cpp
typedef struct {
    int member;
} S2;
extern S2 s2;

// D.cpp
typedef struct {
    int member;
} S2;
S2 s2;

Does this work the same way as the other example?

Well, each typedef declaration introduces the typedef name S2, and [basic.link]/8 tells us that these two declarations declare the same "entity" S2. But we must keep in mind that the kind of "entity" being declared here is a typedef name, not a type. In other words the program only contains one typedef name known as S2. But the question is whether both translation units declare S2 to be aliases to the same type. If not, the program is ill-formed (no diagnostic required).

In other words, in order for this program to be correct, it must be that struct { int member } in C.cpp declares the same type as struct { int member } in D.cpp.

And here, [dcl.typedef]/4 saves us. It tells us that both C.cpp and D.cpp, by defining an unnamed struct in a typedef declaration that makes S2 an alias for that struct, give the struct the name S2 "for linkage purposes". Then, when we use [basic.link]/8 to determine whether those two structs are the same struct, we are instructed to consider them to have the names that they have for linkage purposes. This makes them correspond under [basic.scope.scope]/4, so they declare the same type.

If the "for linkage purposes" rule didn't exist, then they wouldn't correspond (because, if neither one has a name, then we can't claim that they both have the same name) and the program would be ill-formed (no diagnostic required).

If you're wondering why anyone would write typedef struct { int member; } S2; in the first place, the answer is that you probably wouldn't do this in pure C++, but this practice is common in C, and you might be including a C header into your C++ program. So for historical reasons, C++ has to have a special rule to support linking together such declarations.

(But then why does C allow it? It's because C doesn't use "name-based linkage" for types the way that C++ does. It instead uses the concept of "compatible types", which I won't delve into here.)

Brian Bi
  • 111,498
  • 10
  • 176
  • 312
0

I don't think you parsed that correctly. What this is saying that in this kind of declaration the "name for linkage purposes" will be determined this way. You need to have a name for the type if it ever comes up in function arguments somewhere (see What is name mangling, and how does it work? )