Const Static Data Members
const static data members initialized by a constant expression require special attention. Seemingly, the recursive definition ensures that every
const static member whose initializer is a constant expression is also a valid constant expression. This isn't always true, though. Consider the following counter example:
struct C
{
const static int X;
};
const int Y=C::X;
const int C::X=2; //initialized by a constant expression
The static member
X is initialized with a literal. The constant
Y looks as a constant expression because its initializer
C::X is seemingly a constant expression. Well, not quite:
char arr[Y];//error, constant expression required
Actually, neither
C::X nor
Y is a constant expression! The order of their definitions makes all the difference. The definition of
Y uses
C::X as the initializer but at this point,
C::X hasn't been defined yet. Consequently, the
static initialization of
Y is silently replaced with dynamic initialization, which means that
Y is no longer a constant expression. Replacing the order of the definitions fixes this mess:
const int C::X=2;
const int Y=C::X;//Y is now a constant expression
char arr1[Y], arr2[C::X] //OK
Of course, you can eliminate such mishaps by using
in-class initialization:
struct C
{
const static int X=2;
};
This will guarantee that
C::X is a constant expression.
A Programmer's Best Friend
The standards committee is now working on a mechanism that will allow programmers to use inline functions as initializers of constant expressions (further information on this proposal is available here). However, for the time being, you can use a macrounsavory as this may seem.
With respect to const static data members of an integral type, the best policy is to use in-class initializers. However, older compilers don't support in-class initializations. If you're using such a compiler, always initialize const static members before using them or better yet, replace them with nameless enums.
To check whether your constants qualify as constant expressions, use them as initializers of dummy enumerators. This way your loyal compiler becomes the "constant expression watchdog", alerting you whenever an expression that should be a constant expression isn't really so.