17.2 Enumeration Values versus Static Constants
In old C++ compilers, enumeration values were the only available possibility to have true constants (so-called constant-expressions) inside class declarations. However, this has changed during the standardization of C++, which introduced the concept of in-class static constant initializers. A brief example illustrates the construct:
struct TrueConstants { enum { Three = 3 }; static int const Four = 4; };
In this example, Four is a true constantjust as is Three.
With this, our Pow3 metaprogram may also look as follows:
// meta/pow3b.hpp #ifndef POW3_HPP #define POW3_HPP //primary template to compute 3 to the Nth template<int N> class Pow3 { public: static int const result = 3 * Pow3<N-1>::result; }; // full specialization to end the recursion template<> class Pow3<0> { public: static int const result = 1; }; #endif //POW3_HPP
The only difference is the use of static constant members instead of enumeration values. However, there is a drawback with this version: Static constant members are lvalues. So, if you have a declaration such as
void foo(int const&);
and you pass it the result of a metaprogram
foo(Pow3<7>::result);
a compiler must pass the address of Pow3<7>::result, which forces the compiler to instantiate and allocate the definition for the static member. As a result, the computation is no longer limited to a pure compile-time effect.
Enumeration values arent lvalues (that is, they dont have an address). So, when you pass them by reference, no static memory is used. Its almost exactly as if you passed the computed value as a literal. These considerations motivate us to use enumeration values in all metaprograms throughout this book.