Question Details

No question body available.

Tags

c++ c static language-lawyer forward-declaration

Answers (2)

July 10, 2026 Score: 4 Rep: 51,271 Quality: Medium Completeness: 70%

Does anyone know why the restriction seems to be enforced more stringently by C++ compilers?

Because in C++, static int b[]; is a definition which is not allowed to have an incomplete type. The one with extern int a[]; OTOH is a declaration and is allowed to use an incomplete type.

does anyone know of a way of achieving what I tried to do above, namely make a tentative definition of a static array, followed by a real one?

In C++, no because variable definitions can only have complete types(with some exceptions like int arr[]={1,2,3} where the bound will be known from initializer).


Also see Is it possible to forward declare a static array.

July 10, 2026 Score: 1 Rep: 496,044 Quality: Medium Completeness: 60%

C Rules

By the time they started work on the C standard, existing code had started to follow a variety of subtly different rules with respect to things like definitions of static objects, and the delineation between a declaration and a definition.

Just for example, some implementations allowed something like: extern int i; to act as a definition, so even if there wasn't an int i; (without the extern) anywhere, the code would still compile and link.

This more or less forced the C committee to invent the concept of tentative definitions, to allow that existing code to continue working. In the process, they ended up allowing code that most people think should clearly be wrong. For example, if you have something like this at global scope:

int i;
int i;

...that's completely legal in C, even though many (most?) C programmers think it's not (and shouldn't be). Likewise, extern int i; can act as a definition, if there is no other definition of the object, even though most people think of it as purely a declration (which is what it was originally intended to be).

This also creates some rules about the order in which things can appear. For example, this is allowed:

static int i;
extern int i;

...but this is not:

extern int i;
static int i;

Despite some claiming that C is simple and easy to learn, from what I've seen nearly the only people who actually know the rules for tentative definitions in C are compiler authors. And even the people on the committee who wrote the rules don't claim to really undertand them beyod the fact that they did what was necessary to maintain compatibility with most existing code.

C++ Rules

C++ didn't diverge nearly as much before standardization began, so it didn't have to jump through hoops to invent strange rules like tentative definitions to allow it all to contninue working under the standard.

So, initially, the basic rule was pretty simple: if something has extern, it's a declaration. As long as they don't conflict with each other, you can as many declarations as you want. If it doesn't have extern, it's a definition. You can only have one definition of one thing.

C++17 added inline varaibles to C++. inline declarations are a tiny bit like tentative definitions in C, in that you can have something like inline int i; in a header that gets included in a number of different translation units, when ends up defining a single variable that's shared between them. But it (obviously enough) adds a new keyword to signal that intent, and still prohbits things that seem obviously wrong, such as having inline int i; twice in immediate succession.

I haven't thought through it in a lot of detail, but allowing something like tentative definitions in C++ would have the potential for some fairly serious problems. The obvious source of problems would be uncertainty about initialization order. Static initialziation order has always been a problem in C++, and tentative definitions would (potentially) make the problem worse.

Consider something like this:

typedef T / some type /;
typedef U / some type /;

extern T i; extern U j; extern T i;

In C, we don't really care a whole lot about whether i is defined before j or vice versa. Both i and j will be zero-initialized before execution of main commences.

But in C++, initialization can have side effects, so order of initialization matters (often matters a lot). So we need to know whether i is defined before or after j. Worse, if we can't distinguish a declaration from a definition, then something that we wanted to be only a declaration could actually define the variable instead of just declaring it--and in the process, change the order of initialization.

It seems to me that would tend to make code considerably more fragile. If declaring foo also potentially defines it, we probably create circularity problems we can't resolve.