I know that some people are talking about more complex problems than I want to ask with regard to incomplete type. But, let me talk about three fundamental issues:
1) Is incomplete type specification related to C++ only?
see the following snippet.
[root@protos tmp]# cat test.c #include <stdio.h>
int foo[3] = {0, 1, 2};
int main(void) { int i; extern int foo[];
for (i = 0; i < 3; i++) { printf("%d\n", foo[i]); }
for (i = 0; i < 3; i++) { cout << foo[i] << endl; }
return EXIT_SUCCESS;
}
[root@protos tmp]# gcc test.cpp test.cpp: In function 'int main()': test.cpp:11: error: type mismatch with previous external decl of 'int foo []' test.cpp:6: error: previous external decl of 'int foo [3]'
Here, I know that extern in foo[] is invalid due to C++ specification. IMHO, however, everyone knows that extern int foo[] actually means the in foo[3] array defined globally. That means that the size information is known when we see extern int foo[].
Let me think from the perspective of a compiler. 'int foo[3]' precedes 'extern int foo[]' in the code. So, it is manifest that any compiler will see the int foo[3] definition before it sees the 'extern int foo[]'. That means that the extern int foo[] is complete; it's size information
is known in advance. If the lines of my reasoning is true, why does the
compiler raise an error?
3) Why is the size information needed?
Let's think about the code in item 2. In the code, the size of foo array is 3. And I also hardcoded the size of array, 3, in the for loop: i < 3. That means the size information is not needed because C/C++ programmers are responsible for the specifying correct size information like the aforementioned code.
C/C++ does not perform boundary check like JAVA or C#, but the C++ specification requires that the size information should be informed to the compiler. Why the specification postulate that the unneeded information should be provided?
Minkoo Seo wrote: > I know that some people are talking about more complex > problems than I want to ask with regard to incomplete type. > But, let me talk about three fundamental issues: > 1) Is incomplete type specification related to C++ only?
No. Incomplete types are present in both C and C++. I rather believe that the intent in C++ is for them to work the same as in C, at least with regards to arrays. The actual wording in the two standards differs, however, and neither can be said to be a model of clarity.
> see the following snippet. > [root@protos tmp]# cat test.c > #include <stdio.h>
> int foo[3] = {0, 1, 2}; > int main(void) > { > int i; > extern int foo[]; > for (i = 0; i < 3; i++) > { > printf("%d\n", foo[i]); > } > return 0; > } > [root@protos tmp]# gcc test.c > [root@protos tmp]# ./a.out > 0 > 1 > 2 > [root@protos tmp]# cat test.cpp > #include <iostream> > using namespace std; > int foo[3] = {0, 1, 2}; > int main(void) > { > int i; > extern int foo[];
> for (i = 0; i < 3; i++) > { > cout << foo[i] << endl; > } > return EXIT_SUCCESS; > } > [root@protos tmp]# gcc test.cpp > test.cpp: In function 'int main()': > test.cpp:11: error: type mismatch with previous external decl of 'int > foo []' > test.cpp:6: error: previous external decl of 'int foo [3]' > [root@protos tmp]# gcc -v > Using built-in specs. > Target: i386-redhat-linux > Configured with: ../configure --prefix=/usr --mandir=/usr/share/man > --infodir=/usr/share/info --enable-shared --enable-threads=posix > --enable-checking=release --with-system-zlib --enable-__cxa_atexit > --disable-libunwind-exceptions --enable-libgcj-multifile > --enable-languages=c,c++,objc,java,f95,ada --enable-java-awt=gtk > --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre > --host=i386-redhat-linux > Thread model: posix > gcc version 4.0.0 20050519 (Red Hat 4.0.0-8) > [root@protos tmp]# g++ -v > Using built-in specs. > Target: i386-redhat-linux > Configured with: ../configure --prefix=/usr --mandir=/usr/share/man > --infodir=/usr/share/info --enable-shared --enable-threads=posix > --enable-checking=release --with-system-zlib --enable-__cxa_atexit > --disable-libunwind-exceptions --enable-libgcj-multifile > --enable-languages=c,c++,objc,java,f95,ada --enable-java-awt=gtk > --with-java-home=/usr/lib/jvm/java-1.4.2-gcj-1.4.2.0/jre > --host=i386-redhat-linux > Thread model: posix > gcc version 4.0.0 20050519 (Red Hat 4.0.0-8) > [root@protos tmp]# > As you can see, extern int foo[] succeeds in test.c, but > throws an error in test.cpp. Does that mean 'incomplete type > error' is related to C++ only?
My first reaction was that this was a simple compiler bug. A test with Sun CC also showed that the code compiled and worked as expected in C++.
On the other hand, every version of g++ I tried gave the same error message. From what I understand, g++ underwent a major rewrite between versions 2.95.3 and 3.0. Enough of a rewrite to suppose that if the behavior is the same in both 2.95.2 and in more recent versions, it is intentional. And on checking the standard (§8.3.4/1), there is a sentence: "The type 'derived-declarator-type-list array of N of T,' is a different type from the type 'derived-declarator-type-list array of unknown bound of T.'" In addition, in §3.9/7, it says "The declared type of an array object might be an array of unknown size and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points ('array of unknown boudn of T' and 'array of N T') are different types."
On the other hand, there is no doubt that:
extern int foo[] ; // ... int foo[ 3 ] ;
is legal. The only difference here is in the order in which the compilers see the declarations.
Traditionnally, and I believe that this is probably the intent as well, compilers have maintained a list of the information concerning the variable as they went along. If the total information included the dimension data, the type was complete; if not, it wasn't. Regretfully, the actual wording of the standard says that the type may be completed *later*, not that it may be completed *elsewhere*. Interestingly enough, the C standard shares the same problem (§6.2.5/22): "An array type of unknown size is an incomplete type. It is completed, for an identifier of that type, but specifying the size *in* *a* *later* declaration." Taken (too ?) literally, there is a problem in the C code as well.
My immediate reaction is that this is just a case of sloppy wording, and that the real intention must be that once a type is complete, it remains complete, even if later declarations would only be of an incomplete type. But I have to admit that the reason that this is "obvious" to me is 25 years of experience with C and C++ compilers, and not any words I can find in the standard.
[...]
> 3) Why is the size information needed? > Let's think about the code in item 2. In the code, the size of > foo array is 3. And I also hardcoded the size of array, 3, in > the for loop: i < 3. That means the size information is not > needed because C/C++ programmers are responsible for the > specifying correct size information like the aforementioned > code. > C/C++ does not perform boundary check like JAVA or C#, but the > C++ specification requires that the size information should be > informed to the compiler. Why the specification postulate that > the unneeded information should be provided?
The problem isn't that the size information is needed. The error messages you are seeing are for the declaration. The declaration is for an incomplete type. A previous declaration was for a complete type. Incomplete types and complete types are different types. While an incomplete type can be completed (and effectively changes type), the standards (both C and C++) use the word "later" when talking about this. Two interpretations are possible:
-- once the type is complete, it remains complete, even through a declaration which would be for an incomplete type, and
-- once the type is complete, it can no longer be declared incomplete, since this would have the effect of changing a complete type into an incomplete type, a transformation for which the standard makes no provisions.
Personally, only the first interpretation makes any real sense to me. But apparently, the authors of g++ disagree, and have implemented the second. Interestingly enough, in this case, they also disagree with the authors of gcc, although the rule is pretty much the same in the two languages. Still, the wording seems ambiguous enough to me that I think a DR would be in order.
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> > Let's think about the code in item 2. In the code, the size of > > foo array is 3. And I also hardcoded the size of array, 3, in > > the for loop: i < 3. That means the size information is not > > needed because C/C++ programmers are responsible for the > > specifying correct size information like the aforementioned > > code.
> > C/C++ does not perform boundary check like JAVA or C#, but the > > C++ specification requires that the size information should be > > informed to the compiler. Why the specification postulate that > > the unneeded information should be provided?
> The problem isn't that the size information is needed. The > error messages you are seeing are for the declaration. The > declaration is for an incomplete type. A previous declaration > was for a complete type. Incomplete types and complete types > are different types. While an incomplete type can be completed > (and effectively changes type), the standards (both C and C++) > use the word "later" when talking about this. Two > interpretations are possible:
> -- once the type is complete, it remains complete, even through > a declaration which would be for an incomplete type, and
> -- once the type is complete, it can no longer be declared > incomplete, since this would have the effect of changing a > complete type into an incomplete type, a transformation for > which the standard makes no provisions.
> Personally, only the first interpretation makes any real sense > to me. But apparently, the authors of g++ disagree, and have > implemented the second. Interestingly enough, in this case, > they also disagree with the authors of gcc, although the rule is > pretty much the same in the two languages. Still, the wording > seems ambiguous enough to me that I think a DR would be in > order.
[...]
What I am trying to make clear here is the intention of incomplete type. AFAIK, there are two kinds of incomplete type that can occur.
1) struct a; // or class a; 2) int array_name[];
The meaning of incomplete type is quite clear when we talk about the first one, 'struct a', because that's a kind of definition which lacks the actual definition.
However, incompleteness with regard to the second one, int array_name[], does not make sense to me. The reason why the second one is incomplete is that the size of the array is unknown.
As I mentioned previously, however, compilers do not need to know the 'size' of an array in C/C++, because keeping size information is entirely up to programmers.
Why the second type of declaration was sentenced to be incomplete in the standard?
ka...@gabi-soft.fr wrote in message <news:1121686092.664821.234750@g44g2000cwa.googlegroups.com>... > Minkoo Seo wrote: > > I know that some people are talking about more complex > > problems than I want to ask with regard to incomplete type. > > But, let me talk about three fundamental issues:
> > 1) Is incomplete type specification related to C++ only?
> No. Incomplete types are present in both C and C++. I rather > believe that the intent in C++ is for them to work the same as > in C, at least with regards to arrays. The actual wording in > the two standards differs, however, and neither can be said to > be a model of clarity.
> > int main(void) > > { > > int i; > > extern int foo[];
> > for (i = 0; i < 3; i++) > > { > > cout << foo[i] << endl; > > }
> > return EXIT_SUCCESS; > > }
[snip] > > As you can see, extern int foo[] succeeds in test.c, but > > throws an error in test.cpp. Does that mean 'incomplete type > > error' is related to C++ only?
> My first reaction was that this was a simple compiler bug. A > test with Sun CC also showed that the code compiled and worked > as expected in C++.
[snip]
If you put "extern int foo[3] = {0, 1, 2};" into another file, then g++ had no problem. If there is a problem of g++, I think the problem is the interpretation of "extern".
g++ looks for the foo[] definition within the file and reports the type mismatch between foo[] and foo[3]. If g++ left the look-up to the linker, then g++ would not have any problem. I do not know what the Standard says about this.
ka...@gabi-soft.fr wrote: > Minkoo Seo wrote: [...] > > As you can see, extern int foo[] succeeds in test.c, but > > throws an error in test.cpp. Does that mean 'incomplete type > > error' is related to C++ only?
> My first reaction was that this was a simple compiler bug. A > test with Sun CC also showed that the code compiled and worked > as expected in C++.
My first reaction was that this may be related to the stricter scoping in C++. But if this was the case the declaration in main would just hide the namespace scope declaration+definition of foo and the code would still compile without any problems. However, the error message clearly says something different.
[...]
> On the other hand, there is no doubt that:
> extern int foo[] ; > // ... > int foo[ 3 ] ;
> is legal. The only difference here is in the order in which the > compilers see the declarations.
The order doesn't matter. You can have any number of declarations before and/or after the definition.
[...]
> The > error messages you are seeing are for the declaration. The > declaration is for an incomplete type. A previous declaration > was for a complete type.
As if you couldn't write struct A { }; // declaration + definition struct A; // declaration (for an incomplete type???)
> Interestingly enough, in this case, they [authors of g++] also > disagree with the authors of gcc, although the rule is > pretty much the same in the two languages.
If you put it this way, they even disagree with themselves:
extern int foo[]; // OK int foo[]={1,2,3}; extern int foo[]; // OK
void bar(){ extern int foo[]; // g++: ERROR -- SHOULD BE OK
In article <49c1da0b.0507181842.36758...@posting.google.com>, Tokyo Tomy <hos...@jtec.or.jp> writes
>If you put "extern int foo[3] = {0, 1, 2};" into another file, then >g++ had no problem. If there is a problem of g++, I think the problem >is the interpretation of "extern".
Two problems with that
1) extern int foo[3] = {0, 1, 2}; is a definition of foo and not just a declaration. In effect the 'extern' is largely ignored.
In article <1121718646.220404.79...@f14g2000cwb.googlegroups.com>, Minkoo Seo <minkoo....@gmail.com> writes
>What I am trying to make clear here is the intention of >incomplete type. AFAIK, there are two kinds of incomplete >type that can occur.
>1) struct a; // or class a; >2) int array_name[];
>The meaning of incomplete type is quite clear when we talk about >the first one, 'struct a', because that's a kind of definition >which lacks the actual definition.
No, it is a declaration:-) Until the compilers sees a definition that is all it is.
>However, incompleteness with regard to the second one, >int array_name[], does not make sense to me. The reason why the >second one is incomplete is that the size of the array is unknown.
Exactly, perhaps int array_name[]; is just a declaration, it certainly seems to behave much like a declaration rather than a definition.
>As I mentioned previously, however, compilers do not need to know the >'size' of an array in C/C++, because keeping size information is >entirely up to programmers.
But the compiler does need to know the size in two contexts: providing an object (i.e. an actual definition) and applying the sizeof operator which requires a complete type or an expression.
>Why the second type of declaration was sentenced to be incomplete >in the standard?
Because that is exactly what it is, the size of an array is part of its type.
> >1) struct a; // or class a; > >2) int array_name[]; [...]
> >Why the second type of declaration was sentenced to be incomplete > >in the standard?
> Because that is exactly what it is, the size of an array is part of its > type.
I've got a couple of questions out of my ignorance.
1) Does it conform to standard to say that 'the type of int array_name[] is different from int array_name[3]'?
AFAIK, incomplete type like int array_name[] become completed after a compiler sees the definition int array_name[3]. I also believe that such a completing process render [] and [3] everything but a *disparate types*.
2) As to the original question of the first posting:
int array_name[3];
int main() { extern int array_name[]; ...
}
Here, I strongly believe that any compiler sees 'int array_name[3]' before seeing 'extern int array_name[]', because that's the way how lexers works. So, I think 'array_name' become complete when compilers sees the 'extern int array_name[]' declaration, making it a type-compatible declaration(or definition, whatsoever.)
But, opposed to my reasoning, g++ raise an error when above code is compiled. What's the things that I don't understand here?
Minkoo Seo wrote: > Why the second type of declaration was sentenced to be incomplete > in the standard?
Because it is. Saying typedef struct a x; x y; doesn't work because x is incomplete, and saying typedef int x[]; x y; doesn't work either, for the same reason. In either case, sizeof(x) is illegal as well.
Francis Glassborow <fran...@robinton.demon.co.uk> wrote in message <news:XoVaEOPmoN3CFwax@robinton.demon.co.uk>... > In article <49c1da0b.0507181842.36758...@posting.google.com>, Tokyo Tomy > <hos...@jtec.or.jp> writes > >If you put "extern int foo[3] = {0, 1, 2};" into another file, then > >g++ had no problem. If there is a problem of g++, I think the problem > >is the interpretation of "extern". > Two problems with that
> 1) extern int foo[3] = {0, 1, 2}; > is a definition of foo and not just a declaration. In effect the > 'extern' is largely ignored.
> 2) That does not help if you are using sizeof or a template that needs > the complete type. (or I do not think it does)
According to my naive understanding, "extern" requires linker linkag(incomplete type becomes complete type after the linkage), so it is natural for sizeof and template not to work, because they need compile time solution and incomplete type is still incomplete. On this, I said there would be no problems, if a linker linkage was carried out in the original code. (Maybe "extern" should be added to foo[3] = {1, 2, 3})
Francis Glassborow <fran...@robinton.demon.co.uk> wrote in message <news:XoVaEOPmoN3CFwax@robinton.demon.co.uk>... > In article <49c1da0b.0507181842.36758...@posting.google.com>, Tokyo Tomy > <hos...@jtec.or.jp> writes > >If you put "extern int foo[3] = {0, 1, 2};" into another file, then > >g++ had no problem. If there is a problem of g++, I think the problem > >is the interpretation of "extern". > Two problems with that
> 1) extern int foo[3] = {0, 1, 2}; > is a definition of foo and not just a declaration. In effect the > 'extern' is largely ignored.
> 2) That does not help if you are using sizeof or a template that needs > the complete type. (or I do not think it does)
[snip] Continued from my previous mail
I understand the following currently working g++ code would brake. It seems that the code is strange and a surprise to me, but if such codes as below are useful, I should give up my idea.
Vladimir Marko wrote: > ka...@gabi-soft.fr wrote: > > Minkoo Seo wrote: > [...] > > > As you can see, extern int foo[] succeeds in test.c, but > > > throws an error in test.cpp. Does that mean 'incomplete type > > > error' is related to C++ only? > > My first reaction was that this was a simple compiler bug. > > A test with Sun CC also showed that the code compiled and > > worked as expected in C++. > My first reaction was that this may be related to the stricter > scoping in C++. But if this was the case the declaration in > main would just hide the namespace scope > declaration+definition of foo and the code would still compile > without any problems. However, the error message clearly says > something different.
Exactly.
> [...] > > On the other hand, there is no doubt that: > > extern int foo[] ; > > // ... > > int foo[ 3 ] ; > > is legal. The only difference here is in the order in which > > the compilers see the declarations. > The order doesn't matter. You can have any number of > declarations before and/or after the definition.
At least in the standard, there is text describing what happens if an incomplete declaration is *later* completed. And not the slightest idea as to what it means for the type to have been completed before the incomplete declaration.
And this seems to really be the case with g++: if you inverse the order of the definitions of main and foo in his function, g++ compiles it.
> [...] > > The error messages you are seeing are for the declaration. > > The declaration is for an incomplete type. A previous > > declaration was for a complete type. > As if you couldn't write > struct A { }; // declaration + definition > struct A; // declaration (for an incomplete type???)
Interestingly enough:
int foo[3] ; extern int foo[] ;
causes no problems in g++ either. The problem only occurs if the extern is in a function (a nested scope). And of course, if you write "struct A;" in a function, you have declared a new, function local type, and have not named the previously completed type defined at namespace scope.
> > Interestingly enough, in this case, they [authors of g++] > > also disagree with the authors of gcc, although the rule is > > pretty much the same in the two languages. > If you put it this way, they even disagree with themselves: > extern int foo[]; // OK > int foo[]={1,2,3}; > extern int foo[]; // OK > void bar(){ > extern int foo[]; // g++: ERROR -- SHOULD BE OK > } > I've just submitted a bug report: > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=22556 , > extern array: compatible declaration at function scope refused .
I too think that it is a bug in g++. But there are enough other odd things going on here that it is understandable. I don't think their behavior makes sense -- it is definitly wrong. But I'm not really sure on reading the standard what the correct behavior should be.
(I know what I intuitively want it to be, which is pretty much what you seem to think it should be. But what I intuitively want, and what the standard says, are often two different things.)
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Minkoo Seo wrote: > > >1) struct a; // or class a; > > >2) int array_name[]; > [...] > > >Why the second type of declaration was sentenced to be > > >incomplete in the standard? > > Because that is exactly what it is, the size of an array is > > part of its type. > I've got a couple of questions out of my ignorance. > 1) > Does it conform to standard to say that 'the type of int > array_name[] is different from int array_name[3]'?
The standard says so explicitly. In the context of your original code, however, it doesn't say clearly what the consequences should be.
(Note that in the case of g++, scope makes a difference:
int foo[ 3 ]; extern int foo[] ;
is perfectly legal, but putting the second declaration in a function makes it illegal. Which makes me suspect that making it illegal is an accidental bug in g++, and not intentional.)
> AFAIK, incomplete type like int array_name[] become completed > after a compiler sees the definition int array_name[3]. I > also believe that such a completing process render [] and [3] > everything but a *disparate types*.
Once the type is completed, it is completed. Yes. But the standard makes it clear as well that between the incomplete declaration and the moment it becomes completed, the types are different. (Not that many compilers follow the standard on this one.)
> 2) > As to the original question of the first posting: > int array_name[3]; > int main() > { > extern int array_name[]; > ... > } > Here, I strongly believe that any compiler sees 'int > array_name[3]' before seeing 'extern int array_name[]', > because that's the way how lexers works. So, I think > 'array_name' become complete when compilers sees the 'extern > int array_name[]' declaration, making it a type-compatible > declaration(or definition, whatsoever.)
There is (officially, at least) no concept of type compatibility in the C++ standard. Two things are the same type, or they are not. Except that an incomplete type is not the same type as a complete type (in this case), and something that was declared with an incomplete type can have the type completed *later*. (The standard regretfully doesn't say anything as to what the type should be if the type was already complete at the point of the declaration with the incomplete type.)
> But, opposed to my reasoning, g++ raise an error when above > code is compiled. What's the things that I don't understand > here?
I'm tempted to ask: what do you want to understand? The compiler writers don't seem to understand it, so why should you:-)?
Seriously: put your extern's and incomplete types at namespace scope in a header file. Make sure that the header is included before any definition, and you shouldn't have any problems. As for understanding... since it's not at all clear what the standard really says, the only solution is to join the committee and help define it.
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Tokyo Tomy wrote: > Francis Glassborow <fran...@robinton.demon.co.uk> wrote in > message <news:XoVaEOPmoN3CFwax@robinton.demon.co.uk>... > > In article > > <49c1da0b.0507181842.36758...@posting.google.com>, Tokyo > > Tomy <hos...@jtec.or.jp> writes > > >If you put "extern int foo[3] = {0, 1, 2};" into another > > >file, then g++ had no problem. If there is a problem of > > >g++, I think the problem is the interpretation of "extern". > > Two problems with that > > 1) extern int foo[3] = {0, 1, 2}; > > is a definition of foo and not just a declaration. In effect > > the 'extern' is largely ignored.
(Unless the data are const, of course:-).)
> > 2) That does not help if you are using sizeof or a template > > that needs the complete type. (or I do not think it does) > According to my naive understanding, "extern" requires linker > linkag(incomplete type becomes complete type after the > linkage), so it is natural for sizeof and template not to > work, because they need compile time solution and incomplete > type is still incomplete. On this, I said there would be no > problems, if a linker linkage was carried out in the original > code. (Maybe "extern" should be added to foo[3] = {1, 2, 3})
Extern is largely irrelevant here. We're discussing types, and types don't have linkage. extern is a keyword, which may (or may not) affect the "linkage" of the symbol (or one of the symbols in the declaration). It never affects the type, nor for that matter, a symbol which names a type.
That is, I think, part of the problem. When you say "struct C", you name a type. A single, unique type (at least in the relevant scope). Whether that type is complete or not is only a question of what the compiler knows about that type. When you say "int a[]", you do not name a type. You are dealing with a derived type, which doesn't have a simple name. And there are an infinite number of different concrete types which may be used for the definition. Which causes problems for things like function overload resolution and template instantiation, where you need to know the exact type, even if you don't need to know anything about it.
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Minkoo Seo wrote: > Please let me clarify the third point. > [...] > What I am trying to make clear here is the intention of > incomplete type. AFAIK, there are two kinds of incomplete type > that can occur. > 1) struct a; // or class a; > 2) int array_name[];
There's a third. Void is an incomplete type.
> The meaning of incomplete type is quite clear when we talk > about the first one, 'struct a', because that's a kind of > definition which lacks the actual definition. > However, incompleteness with regard to the second one, int > array_name[], does not make sense to me. The reason why the > second one is incomplete is that the size of the array is > unknown.
Because there are some things you cannot do with it. I think the critical aspect is that you cannot create an object with an incomplete type. You might be able have a pointer to the object, or a reference to the object, or a declaration refering to the object, but you cannot have a definition. When you have an incomplete type, some aspect of the type is undefined, and the type must be completed before an object of that type can be defined. (The incomplete type void cannot be completed, and so we can never define an object with type void.)
Beyond that, of course, you are right in that the three types of incomplete types are incomplete in different ways, which results in different constraints. In this case, the most important difference (and the one which is causing the problems), is that the incomplete type 'struct a' is a placeholder for exactly one type. We may not have all of the information concerning that type, but we do know how to 'name' it (e.g. as in name mangling). In the case of 'int a[]', the "incomplete type" is in fact a placeholder for a set of types, and a priori, we don't know how to name it. Except that in certain uses, we have to be able to name it:
template< typename T > vod f( T& a ) ; extern int a[] ;
f( a ) ; // legal, but how to instantiate f?
I presume that this is the motivation for saying that the incomplete type in this case is a distinct type.
Given your initial code:
int foo[ 3 ] = { 0, 1, 2 } ;
int main() { extern int foo[] ; cout << typeid( foo ).name() << std::endl ; return 0 ; }
I see three possible interpretations: it's an error, the type of foo in the typeid is an array[unknown] of int, and the type of foo in the typeid is an array[3] of int. Intuitively, I would expect that latter -- of the three compilers I have at hand (Sun CC, g++ and VC++), g++ treats it as an error, and the other two accept it, and treat it as array[unknown] of int.
If I modify the code to be C (using sizeof, instead of typeid), I get an error from gcc, a warning from Sun cc, and the generated code outputs 0, and VC++ accepts it fine, and outputs 12. Intuitively, I would go with the behavior of VC++ for C, but the standard does seem a bit vague as to what is to be expected.
> As I mentioned previously, however, compilers do not need to > know the 'size' of an array in C/C++, because keeping size > information is entirely up to programmers. > Why the second type of declaration was sentenced to be > incomplete in the standard?
Because it's not complete:-).
The problem, in my mind, is more why it doesn't behave like the other incomplete types; I suspect that it has to do with the fact than an incomplete class has a name, which gives the type itself scope. And any attempt to declare the type in a different scope actually declares a new type -- there's no way to apply the extern modifier to the name of a class.
Having said that, I think you're right in that we have three distinct things (arrays of unspecified dimension, class forward declarations, and void), and calling them all the same thing (an incomplete type) only leads to confusion.
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Francis Glassborow wrote: > In article > <1121718646.220404.79...@f14g2000cwb.googlegroups.com>, Minkoo > Seo <minkoo....@gmail.com> writes > >What I am trying to make clear here is the intention of > >incomplete type. AFAIK, there are two kinds of incomplete > >type that can occur. > >1) struct a; // or class a; > >2) int array_name[]; > >The meaning of incomplete type is quite clear when we talk > >about the first one, 'struct a', because that's a kind of > >definition which lacks the actual definition. > No, it is a declaration:-) Until the compilers sees a > definition that is all it is.
I think he's using the word "definition" somewhat informally, and not in the strict sense it is used in the standard. I think that his point is that there is a difference between the two declarations: the first tells us (and the compiler) almost nothing about the actual type -- we know that it will be a class type, and that's about it. The second actually tells us (and the compiler) quite a bit: we know not only that it will be an array type, but also an array of what.
There are obviously reasons for this, linked at least partially to the strange beast that arrays are in C++. The consequence, however, is that the two cases need to be handled differently in many contexts. Consider, for example, the following:
#include <iostream> #include <ostream>
template< typename T > void f( T const* ) { static int i = 0 ; std::cout << ++ i ; }
// incomplete types. struct S ; extern int a[] ;
void f1() { f( (S*)0 ) ; f( a ) ; }
// complete the types. struct S { int s1 ; int s2 ; } ; int a[ 3 ] ;
According to the standard, this code should output 1121 -- in f1, a has the incomplete type int[], and in f2, it has the complete type int[3]. At least, that is the only reasonable interpretation I can give to §3.9/7: "The declared type of an array object might be an array of unknown size and therefore be incomplete at one point in a translation unit and complete later on; the array types at those two points are different types." But I may have missed something; both Sun CC and g++ output 1122 (presumably using the same template instantiation for the incomplete array in f1 and the complete type in f2).
About all I really feel confident in saying at this point is: 1) the issue is relatively complex, and 2) the standard is far from clear about it. (And in consequence of 2, 3) compilers differ considerably concerning these points.)
> >However, incompleteness with regard to the second one, int > >array_name[], does not make sense to me. The reason why the > >second one is incomplete is that the size of the array is > >unknown. > Exactly, perhaps int array_name[]; is just a declaration, it > certainly seems to behave much like a declaration rather than > a definition.
Who cares. You can't have a definition without having a complete type. But a declaration doesn't necessarily imply an incomplete type. The issue here is incomplete type vs. complete type.
> >As I mentioned previously, however, compilers do not need to > >know the 'size' of an array in C/C++, because keeping size > >information is entirely up to programmers. > But the compiler does need to know the size in two contexts: > providing an object (i.e. an actual definition) and applying > the sizeof operator which requires a complete type or an > expression.
Again, that doesn't invalidate his point. There are things you can do, and things you can't, and they aren't the same depending on the source of the incompletion. Consider:
struct S ; extern int a1[] ; extern S a2[ 5 ] ;
Both arrays have incomplete types. You can, however, index into the first, but not into the second.
You and I both know that this is because an array converts into a pointer to the first element, resulting in an int* -- a complete type -- in one case, and an S* in the other, and that pointer arithmetic is not allowed on an incomplete type. And that indexation is defined in terms of pointer arithmetic. But surely you can see that the issue can be confusing to a beginner.
> >Why the second type of declaration was sentenced to be > >incomplete in the standard? > Because that is exactly what it is, the size of an array is > part of its type.
Or the absence of the size. I find it pretty confusing, myself, that some incomplete types are independant types, whereas others aren't. Apparently, I'm not alone, since Sun CC, g++, gcc and VC++ all do different things in some circomstances.
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
> According to the standard, this code should output 1121 -- in > f1, a has the incomplete type int[], and in f2, it has the > complete type int[3]. At least, that is the only reasonable > interpretation I can give to §3.9/7: "The declared type of an > array object might be an array of unknown size and therefore be > incomplete at one point in a translation unit and complete later > on; the array types at those two points are different types." > But I may have missed something; both Sun CC and g++ output 1122 > (presumably using the same template instantiation for the > incomplete array in f1 and the complete type in f2).
You got this one wrong. The only way to match the template parameters is by using the implicit conversions int[] -> int const* and int[3] -> int const* and the deduced parameters types are S, int, S, int. To get what you really want you must replace f(a) by f(&a) in f1 and f2 and the output _will_ be 1121 (at least with gcc 3.3.1-4.0.0 cygwin, gcc 3.4.2 mingw and MSVC++Toolkit2003).
> I too think that it is a bug in g++. But there are enough other > odd things going on here that it is understandable. I don't > think their behavior makes sense -- it is definitly wrong. But > I'm not really sure on reading the standard what the correct > behavior should be.
> (I know what I intuitively want it to be, which is pretty much > what you seem to think it should be. But what I intuitively > want, and what the standard says, are often two different > things.)
Exactly. And reading the standard I found
3.5/6:
The name of a function declared in block scope, and the name of an object declared by a block scope extern declaration, have linkage. If there is a visible declaration of an entity with linkage having the same name and type, ignoring entities declared outside the innermost enclosing namespace scope, the block scope declaration declares that same entity and receives the linkage of the previous declaration. If there is more than one such matching entity, the program is ill-formed. Otherwise, if no matching entity is found, the block scope entity receives external linkage.
We want (1) to declare+define foo as int[3] (with external linkage) (2) to redeclare foo from the enclosing namespace (3) to be well-formed and bind to foo defined in (1) at compile time (4) to be well-formed (foo is a complete type) But the standard could also be interpreted so that (1) declares+defines foo as int[3] (with external linkage) (2) declares foo at block scope to bind to int foo[] with external linkage in the enclosing namespace (the last sentence of 3.5/6 applies) (3) is well-formed, because of the conversion int[] -> int* (see 8.3.4/6); at link time this will bind to the foo defined in (1) (4) ill-formed, because the foo visible at block scope is incomplete And if this is correct I guess there is a difference between the intent and the wording, the wording being normative.
g++ is wrong to refuse (2) anyway, but the PR's subject may be wrong.
In article <1121935137.190486.127...@z14g2000cwz.googlegroups.com>, Vladimir Marko <swe...@post.sk> writes
>g++ is wrong to refuse (2) anyway, but the PR's subject may >be wrong.
Probably, but I think the programmer is wrong to write it. Consider:
#include <iostream> #include <istream> int a[] = {4,5,6};
int main(){ int a[] = {1,2,3,4}; { extern int a[]; std::cout << a[2]; } std::cout << a[2];
}
To which a should the extern declaration bind? Actually, whatever the Standard says, a programmer who relies on it is walking on very thin ice. There are areas which maybe great fun to test and in doing so we can develop our understanding but we should keep it at that and not seek to use such stressful code in any real program.
Francis Glassborow wrote: > In article <1121935137.190486.127...@z14g2000cwz.googlegroups.com>, > Vladimir Marko <swe...@post.sk> writes > >g++ is wrong to refuse (2) anyway, but the PR's subject may > >be wrong. > Probably, but I think the programmer is wrong to write it. Consider:
thus this a refers to "some a" in the innermost enclosing namespace (see 3.5/6) which happens to be ::a defined in the 3rd line.
> std::cout << a[2];
output 6
> } > std::cout << a[2];
output 3
> } > To which a should the extern declaration bind? Actually, whatever the > Standard says, a programmer who relies on it is walking on very thin > ice. There are areas which maybe great fun to test and in doing so we > can develop our understanding but we should keep it at that and not seek > to use such stressful code in any real program.
True. Especialy for your example. The simple case we were talking about is something along the lines of a function declaration in another function's body. Though I would advise not to use such techniques, someone may find legitimate reasons to do so.
Vladimir Marko wrote: > ka...@gabi-soft.fr wrote: > > #include <iostream> > > #include <ostream> > > template< typename T > > > void f( T const* ) > > { > > static int i = 0 ; > > std::cout << ++ i ; > > } > > // incomplete types. > > struct S ; > > extern int a[] ; > > void > > f1() > > { > > f( (S*)0 ) ; > > f( a ) ; > > } > > // complete the types. > > struct S > > { > > int s1 ; > > int s2 ; > > } ; > > int a[ 3 ] ; > > void > > f2() > > { > > f( (S*)0 ) ; > > f( a ) ; > > } > > int > > main() > > { > > f1() ; > > f2() ; > > return 0 ; > > std::cout << std::endl ; > > return 0 ; > > } > > According to the standard, this code should output 1121 -- > > in f1, a has the incomplete type int[], and in f2, it has > > the complete type int[3]. At least, that is the only > > reasonable interpretation I can give to §3.9/7: "The > > declared type of an array object might be an array of > > unknown size and therefore be incomplete at one point in a > > translation unit and complete later on; the array types at > > those two points are different types." But I may have > > missed something; both Sun CC and g++ output 1122 > > (presumably using the same template instantiation for the > > incomplete array in f1 and the complete type in f2). > You got this one wrong. The only way to match the template > parameters is by using the implicit conversions > int[] -> int const* and int[3] -> int const* > and the deduced parameters types are S, int, S, int. To get > what you really want you must replace f(a) by f(&a) in f1 and > f2 and the output _will_ be 1121 (at least with gcc > 3.3.1-4.0.0 cygwin, gcc 3.4.2 mingw and MSVC++Toolkit2003).
You're right, of course. I changed my idea in mid course, and got it messed up. To avoid the array to pointer conversion, you need to either use references, or an extra level of indirection. My original idea was to use the extra level of indirection, but it didn't work out when I tried to generate the calls, so I switched to counting on references. Except that I forgot to go back and change the template.
At least, I'm happy that at least one compiler agrees with my reading of the standard:-).
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Vladimir Marko wrote: > ka...@gabi-soft.fr wrote: > > Vladimir Marko wrote: > [...] > > > extern int foo[]; // OK > > > int foo[]={1,2,3}; > > > extern int foo[]; // OK > > > void bar(){ > > > extern int foo[]; // g++: ERROR -- SHOULD BE OK > > > } > > > I've just submitted a bug report: > > > http://gcc.gnu.org/bugzilla/show_bug.cgi?id=22556 , > > > extern array: compatible declaration at function scope refused . > > I too think that it is a bug in g++. But there are enough > > other odd things going on here that it is understandable. I > > don't think their behavior makes sense -- it is definitly > > wrong. But I'm not really sure on reading the standard what > > the correct behavior should be. > > (I know what I intuitively want it to be, which is pretty > > much what you seem to think it should be. But what I > > intuitively want, and what the standard says, are often two > > different things.) > Exactly. And reading the standard I found > 3.5/6: > The name of a function declared in block scope, and the > name of an object declared by a block scope extern > declaration, have linkage. If there is a visible > declaration of an entity with linkage having the same > name and type, ignoring entities declared outside the > innermost enclosing namespace scope, the block scope > declaration declares that same entity and receives the > linkage of the previous declaration. If there is more > than one such matching entity, the program is > ill-formed. Otherwise, if no matching entity is found, > the block scope entity receives external linkage.
But this just concerns linkage. I don't think that that's a problem here -- everything has external linkage.
> So, let's have a look on this snippet: > int foo[]={1,2,3}; // (1) > void bar(){ > extern int foo[]; // (2) > foo[2]; // (3) > sizeof(foo); // (4) > } > We want > (1) to declare+define foo as int[3] (with external linkage) > (2) to redeclare foo from the enclosing namespace > (3) to be well-formed and bind to foo defined in (1) at compile > time > (4) to be well-formed (foo is a complete type)
The first three are obvious, I think, and I doubt that any compiler gets them wrong. The real questions are: 1) whether foo is a complete type, and 2) if it isn't, is the declaration at (2) an error, or not?
> But the standard could also be interpreted so that > (1) declares+defines foo as int[3] (with external linkage) > (2) declares foo at block scope to bind to int foo[] with > external linkage in the enclosing namespace (the last > sentence of 3.5/6 applies)
Up until here, there's no doubt as to what is required. All of the compilers conform, too.
> (3) is well-formed, because of the conversion int[] -> int* > (see 8.3.4/6); at link time this will bind to the foo > defined in (1)
Woah. What does the conversion int[] -> int* have to do with anything. This conversion *only* applies in expressions (and not always there). For the moment, there aren't any expressions involving foo.
> (4) ill-formed, because the foo visible at block scope is > incomplete > And if this is correct I guess there is a difference between > the intent and the wording, the wording being normative.
First, I don't really know what the intent was. I think that there was no intent to be incompatible with C here. However, the C standard isn't very clear either, and different C compilers interpret it differently as well.
> g++ is wrong to refuse (2) anyway, but the PR's subject may be > wrong.
I don't think that the original poster is a native English speaker, and I think that he has some trouble expressing himself in English. (I wonder, in fact, if the differences aren't just linguistic, but cultural. And that when he very humbly lists a certain number of questions, a native American wouldn't say something more on the lines "The standard is sure screwed up here.") I think, however, that he has hit upon a very real problem in the standard (and probably a bug in g++ as well).
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Francis Glassborow wrote: > In article <1121935137.190486.127...@z14g2000cwz.googlegroups.com>, > Vladimir Marko <swe...@post.sk> writes > >g++ is wrong to refuse (2) anyway, but the PR's subject may > >be wrong. > Probably, but I think the programmer is wrong to write > it. Consider: > #include <iostream> > #include <istream> > int a[] = {4,5,6}; > int main(){ > int a[] = {1,2,3,4}; > { > extern int a[]; > std::cout << a[2]; > } > std::cout << a[2]; > } > To which a should the extern declaration bind?
The extern declaration can only bind to something which has external linkage. There's no question about that. The definition of a in main is a red herring. The extern declaration of a also hides all declarations in outer scope; I believe that this is the reasoning behind the behavior of the EDG front end when it rejects a sizeof on a.
> Actually, whatever the Standard says, a programmer who relies > on it is walking on very thin ice.
Relies on what? I think most people would agree that block scope is not the best place for extern declarations. The orginal poster didn't claim that the code was "good", either. He asked (or at least to me, it seemed like he was asking) a question about the standard.
> There are areas which maybe great fun to test and in doing so > we can develop our understanding but we should keep it at that > and not seek to use such stressful code in any real program.
It's not so much a question of "stress" but conventions. extern declarations belong at namespace scope, in headers. And no where else. That's the usual coding convention. But from a standards point of view, I think that there is a valid question.
-- James Kanze GABI Software Conseils en informatique orientée objet/ Beratung in objektorientierter Datenverarbeitung 9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34