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]);
}
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?
2) Why the compiler can't find the size information?
See the code.
[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]'
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?
Regards,
Minkoo Seo.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]
> 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.
> return 0;
> }
> using namespace std;
> return EXIT_SUCCESS;
> }
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
[...]
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?
Regards,
Minkoo Seo
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.
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
}
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 .
Regards,
Vladimir Marko
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)
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
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.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
You did not go far enough because you did not attempt to use foo, try:
int foo[3];
int main(){
extern int foo[];
sizeof(foo);
}
I seem to remember that the C usage of extern is slightly different to
C++'s and gets tangled up with C's weird 'tentative definitions'.
--
Francis Glassborow ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
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?
Regards,
Minkoo Seo
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.
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})
[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.
#include <iostream>
using namespace std;
class C {
int i;
} c;
int main(void)
{
extern class C c;
cout << sizeof(c) << endl;
return EXIT_SUCCESS;
> > 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
> > >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
> > >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
> [...]
> 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
> >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 ] ;
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).
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
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).
Vladimir Marko
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.
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)
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.
Regards,
Vladimir Marko
#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 ACCU
Author of 'You Can Do It!' see http://www.spellen.org/youcandoit
For project ideas and contributions: http://www.spellen.org/youcandoit/projects
This a is not visible
>
> int main(){
> int a[] = {1,2,3,4};
and this a has no linkage,
> {
> extern int a[];
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
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
> 3.5/6:
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
> #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
You're right. And that makes me think that the "we want" section
below is actualy wrong starting from (2).
> > 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?
The more I think of it the less I want (2) to redeclare (1). The
other option (discussed below) just makes more sense to me. And
I can't see a single reason for (2) to be an error.
> > 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.
Did you notice the difference between this and a "redeclaration"?
The local foo is not related to the global foo in any way until
the link time.
> > (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.
8.3.4/6 says that "except where it has been declared for a class,
the subscript operator [] is interpreted in such a way that E1[E2]
is interpreted as *((E1)+(E2))". And *(foo+2) requires the
conversion. Or, perhaps, did you mean that "foo[2]" is not an
expression?
> > (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.
Maybe there was no explicit intent at all, because nobody really
studied this issue. In such a case C compatibility seems like a
good implicit intent. And I have a bad feeling that what makes
most sense to me right now need not be compatible with C (well,
if C was clear about this issue).
> > 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).
I think you misunderstood me. (I'm not a native English speaker,
are you?) You wrote about the "original poster" (or "OP") which
is Minkoo Seo, right? So I guess you understood "subject" as
a reference to this thread's subject (i.e. "incomplete type
revisited"). On the other hand I wrote about the subject of the
"PR" (or "problem report"). Given that there is "g++" on the
same line I thought it would be clear that I referred to this:
> > > > 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 .
Regards,
Vladimir Marko
> [...]
>>>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?
> The more I think of it the less I want (2) to redeclare (1).
I'm not sure what you actually mean by this. According to the
standard, 2 is a declaration of a variable with the name foo.
This name has external linkage, and binds to the definition in
1. That much is clear.
The more I think about it, the more I tend to think that in
fact, that sizeof(foo) in (4) probably should be illegal
(although that was not my original reaction). Basically, the
declaration of foo in (2) hides any outer declarations, so from
then on, that is all that the compiler sees.
> The other option (discussed below) just makes more sense to
> me. And I can't see a single reason for (2) to be an error.
>>>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.
> Did you notice the difference between this and a
> "redeclaration"? The local foo is not related to the global
> foo in any way until the link time.
The local foo and the global foo are names refering to the same
object.
>>> (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.
> 8.3.4/6 says that "except where it has been declared for a
> class, the subscript operator [] is interpreted in such a way
> that E1[E2] is interpreted as *((E1)+(E2))".
Sorry. I don't know what I was looking at when I wrote this,
but it wasn't (3). Despite the fact that you wrote it out in
big letters.
Obviously, in the declarations, [] isn't an operator, but (3)
isn't a declaration, but an expression statement (with no side
effects, which is rather unusual, but perfectly legal).
>>> (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.
> Maybe there was no explicit intent at all, because nobody
> really studied this issue. In such a case C compatibility
> seems like a good implicit intent. And I have a bad feeling
> that what makes most sense to me right now need not be
> compatible with C (well, if C was clear about this issue).
Now, now, we mustn't say things like that:-). (Actually, I
agree. I don't think anyone considered the case where a
declaration with an incomplete type "linked" to a definition in
which the type was complete.)
>>>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).
> I think you misunderstood me. (I'm not a native English
> speaker, are you?)
Sort of. I grew up speaking English, but I've not used it
regularly for 35 years now, and I feal more comfortable with
French, and maybe even German (spoken at least).
> You wrote about the "original poster" (or "OP") which is
> Minkoo Seo, right? So I guess you understood "subject" as a
> reference to this thread's subject (i.e. "incomplete type
> revisited"). On the other hand I wrote about the subject of
> the "PR" (or "problem report"). Given that there is "g++" on
> the same line I thought it would be clear that I referred to
> this:
OK. I didn't recognize the abbreviation PR, and assumed that it
must be an alternative abbreviation for OP (with the P being
poster, and the R somthing I couldn't guess).
--
James Kanze mailto: james...@free.fr
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 pl. Pierre Sémard, 78210 St.-Cyr-l'École, France +33 (0)1 30 23 00 34
The only thing that still bothers me is the internal linkage
of objects declared extern (3.5/6):
static int foo0; // internal linkage
static int foo[]={1,2,3}; // internal linkage
void bar(){
extern int foo0; // internal linkage!!!
extern int foo[]; // internal/external/undefined behaviour?
// (int[3] and int[] are different types)
}
When I say "extern" I mean it. So why do I get the internal
linkage? And, more importantly, why does the order of "foo0"
and "bar" matter?
But these are questions for another thread (perhaps even
another newsgroup -- comp.std.c++).
Vladimir Marko
Linkage concerns the name, not the type (more or less --
typically, it concerns the mangled name, although that's
obviously not the way the standard words it).
> }
> When I say "extern" I mean it. So why do I get the internal
> linkage?
Because the standard says so:-).
It's hard to really say what it should mean. I suspect that you
won't find much interest in the standards committee for changing
what it currently means, however. On one hand, changing it
could break currently legal code, and on the other, I think that
there is, today, pretty much a consensus that you shouldn't put
extern declarations in function scope -- they belong in a
header, at namespace scope.
> And, more importantly, why does the order of "foo0"
> and "bar" matter?
> But these are questions for another thread (perhaps even
> another newsgroup -- comp.std.c++).
In my original example? If sizeof(foo) is to be legal, it
matters. If not, it shouldn't matter. In your example, because
the standard says so : a block scope extern does not change the
linkage of a previouslly declared variable, and it may bind to a
variable which doesn't have external linkage. Throw in a few
namespaces, and the results probably become even wierder; I'm
not sure what the following is supposed to mean:
static int foo() ;
namespace X {
void bar()
{
extern int ::foo() ; // ???
}
}
All in all, however, I don't think it that important. The
coding guidelines say no extern declarations and no function
declarations at block scope. Problem solved.
--
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
Could you tell me where can I find a nice c++ coding guidelines?
I think I need to read it thoroughly.
Regards,
Minkoo Seo
> Could you tell me where can I find a nice c++ coding
> guidelines? I think I need to read it thoroughly.
I'm tempted to say that it's important that your shop write its
own. That way, you understand the reason behind each rule.
Still, reading others can give you some good ideas.
The Sutter and Alexandrescu book seems to be the reference
today, although I imagine you'd still want to personalize it for
your shop. Traditionnally, the Ellemtel guidelines were
considered the reference. I don't know if its been kept up to
date, but I think it still worth looking at, if only because it
represents real coding guidelines in a real organization. Other
than that, there's a list of coding standards in the FAQ:
http://new-brunswick.net/workshop/c++/faq/coding-standards.html#faq-27.12.
(Although personally, I don't like to call them standards. Just
guidelines.)
--
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