问题提出:
在开发 C++ 版几何图形系统中, 需要求出两个数字的最大公因子, 算法用 C 语言可写出为:
int gcd (int x, int y) {
if (y == 0) return x;
else if (x > y) return gcd (y, x % y);
else /* x <= y */ return gcd (x, y % y);
}
但是为定义一个静态的数组如 char buf[size], 这里 size 是两个编译期已知的常量的最大公因子
的时候, 就无法用函数来计算了, 因为函数要到运行期才能计算出结果. 而我们需要在编译期就知道
两个数的最大公因子.
因此, 我们必须有什么办法根据已知的两个常量, 静态的计算出它们的最大公因子.
例如用宏的形式写为 #define GCD(x, y) ...某求出gcd的方法...
在 C 语言中, 是没有办法做到求 gcd 的静态写法的. 用 C++ 的模板, 才能做到.
为了实现求 gcd, 需要先做一些准备工作. 包括求两个值的较大者, 较小者, 以及两个数的模.
#define min(x, y) ((x) < (y) ? (x) : (y)) // 求两者中较小者.
#define max(x, y) ((x) > (y) ? (x) : (y)) // 求两者中较大者.
求两者的余 mod 需要用模板, 因为对 0 求除会导致错误, 在常量表达式中不能使用.
template <int X, int Y> struct mod {
enum { value = X % Y };
};
解决除 0 的问题, 用模板的偏特化版本:
template <int X> struct mod <X, 0> { // X % 0
enum { value = 0 };
};
template <int Y> struct mod <0, Y> { // 0 % Y
enum { value = 0 };
};
template <> struct mod<0, 0> { // 0 % 0
enum { value = 0 };
};
简单测试 mod:
enum {
mod1 = mod<3, 4>::value, // ==> 3, 使用一般版本的 mod<>
mod2 = mod<4, 3>::value, // ==> 1, 一般版本
mod3 = mod<0, 4>::value, // ==> 0, 偏特化 x=0 的版本.
mod4 = mod<3, 0>::value, // ==> 0, 偏特化 y=0 的版本.
mod5 = mod<0, 0>::value, // ==> 0, 特化 x=0,y=0 的版本.
};
然后将 gcd 算法翻译为模板:
template <int X, int Y> struct gcd {
enum { amin = min (X, Y), // X, Y 中较小的一个.
amax = max (X, Y), // X, Y 中较大的一个.
amod = mod<amax, amin>::value, // X % Y 如果 X > Y
value = (Y == 0) ? X
: gcd <amin, amod>::value // 递归求小者 和 模 的 gcd.
};
};
再提供两个特化的版本:
template <> struct gcd <1, 1> { enum { value = 1 }; };
template <> struct gcd <0, 0> { enum { value = 0 }; };
(也许还能提供更多的特化版本).
测试 gcd:
enum {
gcd1 = gcd<4, 3>::value, // ==> 1
gcd2 = gcd<4, 6>::value, // ==> 2
gcd3 = gcd<20, 16>::value, // ==> 4
};
现在我们可以定义一个静态数组, 用如下方式了:
#define X 4 // X, Y 根据编译器参数可能有所不同. 例如 X 定义为 sizeof (int)
#define Y 6
static const int buf_size = gcd<4, 6>::value; // ==> 2
static int buf [buf_size];
最后基于 gcd 以求出最小公倍数 lcm:
#define lcm(X, Y) ((X)/gcd<(X), (Y)>::value * (Y))
enum {
lcm1 = lcm(4, 6), // ==> 12
lcm2 = lcm(3, 7), // ==> 21
};