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