Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |
In 1993 compilers were beginning to support simple template
constructs so that users could define generic containers and functions. About
the same time that the STL was being considered for adoption into Standard C++,
clever and surprising examples such as the following were passed around among
members of the C++ Standards Committee:
//: C05:Factorial.cpp
// Compile-time computation using templates.
#include <iostream>
using namespace std;
template<int n> struct Factorial {
enum { val = Factorial<n-1>::val * n };
};
template<> struct Factorial<0> {
enum { val = 1 };
};
int main() {
cout << Factorial<12>::val << endl;
// 479001600
} ///:~
That this program prints the correct value of 12! is
not alarming. What is alarming is that the computation is complete before the
program even runs!
When the compiler attempts to instantiate Factorial<12>,
it finds it must also instantiate Factorial<11>, which requires Factorial<10>,
and so on. Eventually the recursion ends with the specialization Factorial<1>,
and the computation unwinds. Eventually, Factorial<12>::val is
replaced by the integral constant 479001600, and compilation ends. Since all
the computation is done by the compiler, the values involved must be
compile-time constants, hence the use of enum. When the program runs,
the only work left to do is print that constant followed by a newline. To
convince yourself that a specialization of Factorial results in the
correct compile-time value, you could use it as an array dimension, such as:
double nums[Factorial<5>::val];
assert(sizeof
nums == sizeof(double)*120);
Thinking in C++ Vol 2 - Practical Programming |
Prev |
Home |
Next |