I read the reason for defining static data members in the source file is because if they were in the header file and multiple source files included the header file- the definitions would get output multiple times. I can see why this would be a problem for the static const data member, but why is this a problem for the static data member?
I'm not too sure I fully understand why there is a problem if the definition is written in the header file...
I think you're confusing two things: static
data members and global variables markes as static
.
The latter have internal linkage, which means that if you put their definition in a header file that multiple translation units #include
, each translation unit will receive a private copy of those variables.
Global variables marked as const
have internal linkage by default, so you won't need to specify static
explicitly for those. Hence, the linker won't complain about multiple definitions of global const
variable or of global non-const
variables marked as static
, while it will complain in the other cases (because those variables would have external linkage).
Concerning static
data members, this is what Paragraph 9.4.2/5 of the C++11 Standard says:
static
data members of a class in namespace scope have external linkage (3.5). A local class shall not have
static
data members.
This means that if you put their definition in a header file #include
d by multiple translation units, you will end up with multiple definitions of the same symbol in the corresponding object files (exactly like non-const
global variables), no matter what their const
-qualification is. In that case, your program would violate the One Definition Rule.
Also, this Q&A on StackOverflow may give you a clearer understanding of the subject.
The multiple definition problem for variables is due to two main deficiencies in the language definition.
As shown below you can easily work around it. There is no technical reason why there is no direct support. It has to do with the feature not being in sufficient high demand that people on the committee have chosen to make it a priority.
First, why multiple definitions in general are a problem. Since C++ lacks support for separately compiled modules (deficiency #1), programmers have to emulate that feature by using textual preprocessing etc. And then it's easy to inadvertently introduce two or more definitions of the same name, which would most likely be in error.
For functions this was solved by the inline
keyword and property. A freestanding function can only be explicitly inline
, while a member function can be implicitly inline
by being defined in the class definition. Either way, if a function is inline
then it can be defined in multiple translation units, and it must be defined in every translation unit where it's used, and those definitions must be equivalent.
Mainly that solution allowed classes to be defined in header files.
No such language feature was needed to support data, variables defined in header files, so it just isn't there: you can't have inline
variables. This is language deficiency #2.
However, you can obtain the effect of inline
variables via a special exemption for static
data members of class templates. The reason for the exemption is that class templates generally have to be fully defined in header files (unless the template is only used internally in a translation unit), and so for a class template to be able to have static
data members, it's necessary with either an exemption from the general rules, or some special support. The committee chose the exemption-from-the-rules route.
template< class Dummy >
struct math_
{
static double const pi;
};
template< class Dummy >
double const math_<Dummy>::pi = 3.14;
typedef math_<void> math;
The above has been referred to as the templated const trick. As far as I know I was the one who once introduced it, in the [comp.lang.c++] Usenet group, so I can't give credit to someone else. I've also posted it a few times here on SO.
Anyway, this means that every C++ compiler and linker internally supports and must support the machinery needed for inline
data, and yet the language doesn't have that feature.
However, on the third hand, C++11 has constexpr
, where you can write the above as just
struct math
{
static double constexpr pi = 3.14;
};
Well, there is a difference, that you can't take the address of the C++11 math::pi
, but that's a very minor limitation.