This is the mail archive of the
gcc-help@gcc.gnu.org
mailing list for the GCC project.
RE: C++ static integer class constants...
- From: "John Ratliff" <webmaster at technoplaza dot net>
- To: <gcc-help at gcc dot gnu dot org>
- Date: Sun, 16 Oct 2005 14:45:33 -0500
- Subject: RE: C++ static integer class constants...
I forgot to hit reply to all, but I wanted this to go to the list.
> After reading Ryan's post, I looked at the (draft) standard. It says:
>
> 4 If a static data member is of const integral or const enumeration
> type, its declaration in the class definition can specify a constant-
> initializer which shall be an integral constant expression
> (_expr.const_). In that case, the member can appear in integral con-
> stant expressions within its scope. The member shall still be defined
> in a namespace scope if it is used in the program and the namespace
> scope definition shall not contain an initializer.
>
> If I understand this correctly, even const static members need to have
> a definition somewhere. Maybe this is "needed" so that you may always
> take the address of such const static members.
>
> So the compiler is free to use the constant as if they were variables,
> if it needs to. Your code compiles OK if the line
>
> int offset = (redundant ? B : A);
>
> is replaced by
>
> if(redundant)
> offset = B;
> else
> offset = A;
>
> I don't know why, but it does work here.
This seems like a definite bug. The fact that it works in 3.4.2 and not in
3.3.3 seems like a probable bug to me, but I will ask this question in a
standard C++ newsgroup so that someone intimately familiar with the standard
can give me a definitive answer, but I still think g++ 3.3 is wrong and g++
3.4 is right.
If I were taking the address of the constant, sure, it would have to have
storage space and I wouldn't expect it any other way.
I was looking at the assembly generated by each compiler. There is a
striking difference. Here is foo::method(bool redundant) in x86 assembly
generated by mingw/g++ 3.4.2. I have commented the relevant sections.
-----------------------------------------------
.globl __ZNK3foo6methodEb
.def __ZNK3foo6methodEb; .scl 2; .type 32;
.endef
__ZNK3foo6methodEb:
pushl %ebp
movl %esp, %ebp
subl $40, %esp
movl 12(%ebp), %eax
movb %al, -1(%ebp) ; move redundant (%al) to stack
cmpb $0, -1(%ebp) ; if redundant is false
je L12 ; goto L12
movl $8184, -16(%ebp) ; move 8184 (0x1FF8) into -16(%ebp) (aka
offset)
jmp L13 ; goto L13
L12:
movl $8, -16(%ebp) ; move 8 into offset
L13:
movl -16(%ebp), %eax ; return std::pair blah blah blah
movl %eax, -8(%ebp)
movl 8(%ebp), %eax
addl -8(%ebp), %eax
incl %eax
movzbl (%eax), %eax
movb %al, -11(%ebp)
leal -11(%ebp), %eax
movl %eax, 8(%esp)
movl 8(%ebp), %edx
movl -8(%ebp), %eax
movzbl (%eax,%edx), %eax
movb %al, -12(%ebp)
leal -12(%ebp), %eax
movl %eax, 4(%esp)
leal -10(%ebp), %eax
movl %eax, (%esp)
call __ZNSt4pairIhhEC1ERKhS2_
movzwl -10(%ebp), %eax
leave
ret
-----------------------------------------------
And here is g++ 3.3.3's assembly output. As you can see, it's not even
trying to use it as a constant. This is why storage space is required.
-----------------------------------------------
_ZNK3foo6methodEb:
.LFB1535:
pushl %ebp
.LCFI14:
movl %esp, %ebp
.LCFI15:
subl $24, %esp
.LCFI16:
movl 16(%ebp), %eax
movb %al, -1(%ebp) ; move al (redundant) to stack
cmpb $0, -1(%ebp) ; is redundant false?
je .L6 ; then jump to .L6
movl _ZN3foo1BE, %eax ; move B (storage space required) to eax
movl %eax, -16(%ebp) ; put eax on the stack
jmp .L7
.L6:
movl _ZN3foo1AE, %eax ; move A (storage space required) to eax
movl %eax, -16(%ebp) ; and put eax on the stack
.L7:
movl -16(%ebp), %eax ; ??? why is it moving it just to move it
back?
movl %eax, -8(%ebp) ; whatever, the rest is return std::pair
blah blah
subl $4, %esp
movl 12(%ebp), %eax
addl -8(%ebp), %eax
incl %eax
movb (%eax), %al
movb %al, -9(%ebp)
leal -9(%ebp), %eax
pushl %eax
movl 12(%ebp), %edx
movl -8(%ebp), %eax
movb (%eax,%edx), %al
movb %al, -10(%ebp)
leal -10(%ebp), %eax
pushl %eax
pushl 8(%ebp)
.LCFI17:
call _ZNSt4pairIhhEC1ERKhS2_
addl $16, %esp
movl 8(%ebp), %eax
leave
ret $4
-----------------------------------------------
If you replace the ternary operator with the if as you suggested, the
assembly looks more like the 3.4.2 version. This is why it compiles, and
makes it seem more and more like a bug. Clearly it's capable of doing what I
think it should do, but chooses not to when presented with the ternary
operator.
>
> So you need to define those constants somewhere, but the compiler may
> use the initializer in the class scope, if it is able to do so. For
> some reason, GCC couldn't use the initializer in the first case, so it
> is using the address of the constants. Go figure.
The assembly output and changing from ternary to if proves it can use the
initializer, but chooses not to.
Thanks,
--John Ratliff