I know that the C++ standard says (sec 9.4.2 paragraph 4) that a static member variable of integral or enum type can provide an initializer inside the class, but that this requires a definition of that member outside the class (in a compilation unit). I.e., you need to do something like this:
class A
{
public:
static const int X = 10;
};
// this is required by the standard
const int A::X;
I’ve seen (and I’ve seen it said other places) that some compilers will let you get away without the outside-of-class definition. This works on gcc 4.2.1 on OS X:
#include <iostream>
class A
{
public:
static const int X = 10;
};
int main(int argc, char** argv)
{
std::cout << A::X << std::endl;
return 0;
}
I recently encountered a bug where someone had done this, but they were using the member variable inside a templated function (std::max to be exact), and it would NOT compile, complaining about the undefined symbol A::X. I.e., this doesn’t work:
#include <iostream>
#include <algorithm>
class A
{
public:
static const int X = 10;
};
int main(int argc, char** argv)
{
std::cout << std::max(1, A::X) << std::endl;
return 0;
}
Adding back in the outside-of-class definition makes it work.
This is sort of an academic question, but I’d like to know why this happens. Especially in relation to the fact that if we replace the static member variable with a static function (static const int X = 10; becomes static int X(), A::X becomes A::X()), then it will compile without the outside-of-class definition. The reason I mention templates is because std::max is templated, and other templated functions reproduce the same behavior. It may not be specifically related to templates, but I’d like to understand why it is that templates cause the behavior that they do. I assume this must have to do with the way that templates and static members get compiled/implemented?
PS – I posted some minimal code on github
It will compile without a definition.
The definition is needed at link time, if the static member variable is odr-used. (It wouldn’t be odr-used if the compiler managed to substitute its actual value every time it was referenced. On the other hand, taking its address is sure to make it odr-used)
This is the complete rule (section 9.4.2
[class.static.data]):and from section 3.2
[basic.def.odr]:The requirements for appearing in a constant expression ARE satisfied, so everything depends on whether it is used as an lvalue or rvalue.
std::maxtakes an lvalue reference, so there is not an immediate lvalue-to-rvalue conversion.The only interaction with templates is that there could be multiple equivalent definitions and the linker will pick any one. In your case, there is no template, so multiple definitions would produce a “symbol multiply defined” type of error.
When you forget to provide a definition, both cases (member of template class and member of ordinary class) will give the same error: “undefined symbol”.