14

I have some C code that I have to port to C++. The code has a structure

struct A { 
    ...
    struct A * myPtr;
}

And now two global arrays are declared and initialized like this:

//Forward declaration of Unit
struct A Unit[10];

struct A* ptrUnit[2] = { Unit, Unit+7 };
struct A Unit[10] = { { .., &ptrUnit[0] }, 
                      ... };

Now while this works fine in C, it gives an error in C++ (variable redeclared). Aren't variables allowed to be forward-declared in C++?

Omair
  • 814
  • 1
  • 10
  • 19

4 Answers4

24

struct A Unit[10] is not a forward declaration of a variable. The term "forward declaration" normally refers to non-defining declarations, while struct A Unit[10] is a definition. So in your code you are defining Unit multiple times in the same source file. In C language it is allowed, since in C definitions without an initializer are tentative definitions. They may occur many times in the same translation unit. In C++ there's no such thing as tentative definition. In C++ multiple definitions are always illegal.

If you want a genuine forward declaration for a variable, you have to use the keyword extern

extern struct A Unit[10];

This will work in both C and C++. However, as a side effect, this will give Unit external linkage. If you need a variable with internal linkage, then you are out of luck in C++, since in C++ it is not possible to forward-declare a variable with internal linkage. Meanwhile, in C tentative definitions will still help you to achieve that.

AnT stands with Russia
  • 312,472
  • 42
  • 525
  • 765
  • For internal linkage in С++ you can use something like this: `namespace { extern struct A Unit[10]; }`. I guess it is not really internal linkage, but `Unit` will be available only in the current translation unit anyway. https://stackoverflow.com/a/15419573/5447906 – anton_rh May 17 '23 at 15:55
21

In C++, a variable declaration must be prefixed with extern:

extern A Unit[10];

// ...

A Unit[10] = { ... };

(Note that in C++ you can omit the leading struct.)

sbi
  • 219,715
  • 46
  • 258
  • 445
  • 2
    See [this answer](http://stackoverflow.com/questions/1410563/what-is-the-difference-between-a-definition-and-a-declaration/1410632#1410632) for more details on declarations and definitions. – sbi Sep 15 '10 at 05:09
  • 2
    your statement about `the same namespace` is not completely correct. A `struct A` definition has an implicit `typedef struct A A` as a consequence, if the identifier `A` is not defined in the same scope. Something like `struct A { ... } A;` is completely legal in C++, and then `A` (without `struct`) refers to the object. That this is possible, doesn't meant that this is good style, though. In POSIX you have that for example with the "sys/stat.h" header file that defines `struct stat` and the function `stat`. – Jens Gustedt Sep 15 '10 at 06:26
  • 1
    @Jens: I'm not sure on the legal details of this, but you seem to have a point. I edited my answer accordingly. – sbi Sep 15 '10 at 08:00
  • 2
    @Jens Gustedt: They still live in different identifier spaces, and there is no implicit `typedef` (else the symbol would also be defined in the global identifier space). The difference between C and C++ here is that if a symbol is not located in the identifier space, it will also be looked up in the user defined types. There is a slightly longer explanation in this answer I wrote some time ago: http://stackoverflow.com/questions/1675351/typedef-struct-vs-struct-definitions/1675446#1675446 – David Rodríguez - dribeas Sep 15 '10 at 08:02
  • 1
    @David: thanks for the pointer. The rule for C++ is even worse than I thought. – Jens Gustedt Sep 15 '10 at 09:50
1

C allows variables to be tenatively declared (I guess). C++ does not. Once 'Unit' has been defined, it cannot be redefined in the same scope

Chubsdad
  • 24,777
  • 4
  • 73
  • 129
0

Make Unit a function that returns a reference to to A[10] and have the actual array be a static variable in the function.

// in hpp file
A[10]& Unit();

// in cpp file
A[10]& Unit() {
    static A[10] value;
    return value;
}
Michael
  • 1,263
  • 1
  • 12
  • 28