views:

234

answers:

4

I'm having trouble deciding whether not this code should compile or if just both compilers I tried have a bug (GCC 4.2 and Sun Studio 12). In general, if you have a static class member you declare in a header file you are required to define it in some source file. However, an exception is made in the standard for static const integrals. For example, this is allowed:

#include <iostream>

struct A {
    static const int x = 42;
};

With no need to add a definition of x outside the class body somewhere. I'm trying to do the same thing, but I also take the address of x and pass it to a template. This results in a linker error complaining about a lack of definition. The below example doesn't link (missing a definition for A::x) even when it's all in the same source file:

#include <iostream>

template<const int* y>
struct B {
    static void foo() { std::cout << "y: " << y << std::endl; }
};

struct A {
    static const int x = 42;
    typedef B<&x> fooness;
};

int main()
{
    std::cout << A::x << std::endl;
    A::fooness::foo();
}

Which is bizarre since it works as long as I don't pass the address to a template. Is this a bug or somehow technically standards compliant?

Edit: I should point out that &A::x is not a runtime value. Memory is set aside for statically allocated variables at compile time.

+1  A: 

You are trying to pass a runtime value to a template, that's not possible. The only allowed template parameters are types (class/typename) or integral constant values (int/bool/etc).

Andreas Magnusson
It's not a runtime value. Static variables are allocated at compile time. With definition for A::x outside the class this will compile fine.
Joseph Garvin
The address of a variable cannot be known until it is loaded in memory, me thinks.
GMan
Like I said, with a definition this compiles. Things with external linkage (like a static member of a class) get their addresses patched by the linker/loader (I think at which step depends on the system) appropriately. You can verify this by passing the addresses of globals and static to templates in general -- it usually works, just not in this special case.
Joseph Garvin
A: 

Interesting, it compiled fine for me on VS 2008. I kind of assumed that the error came from the typedef because at compile time when it tries to compile 'B' with &x as the template type it doesn't then know where the address of x will be. Still... it compiles and gives a reasonable output.

DeusAduro
A: 

I could see how one might expect this to compile anyway.

The address of a static const isn't really a runtime value and can be fully resolved at link time.

Joshua
Except that these inline static const ints don't really exist unless you take the address. There is absolutely no reason for it to occupy memory otherwise.
Zan Lynx
+3  A: 

To be a well formed program you stil have to have the defintion of the static variable (without an initializer in this case) if it actually gets used, and taking the address counts as a use:

  • C++2003 Standard: 9.4.2 Static data members Paragraph 4 (bold added)

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 (5.19). In that case, the member can appear in integral constant expressions. 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

Michael Burr
Funny that Visual Studio lets you get away with it but not GCC :(
Joseph Garvin
The beauty of undefined behavior is that you might just get the results you want... just not always.
Michael Burr