模板元编程在立体几何中的妙用
为了更好地理解三维游戏编程,我开始研究了立体几何,注意,是立体解析几何,里面涉及到了很多元组、向量和矩阵的知识。虽然还有一些不懂,可是这唤醒了我在高等数学中学到的知识,我想以后还是有很大的用处的。
当然,数学是工具,是为我们编程服务的。但是出于对性能和简洁性的敏感,在构建数学库的过程中我千方百计减少我们的代码量。在我所学的封装、继承和多态以及模板中进行选择,最后我尝试了一下模板。
我学习了一下模板,发现这是一个非常好的特性。使用模板偏特化的技术,可以让编译器在编译器为我们做一些事情。我们先了解一下,这是在博客园中salomon的一篇介绍模板元编程中的例子,没错,就是求菲波那锲数列的和。
// 主模板 template<int N> struct Fib { enum { Result = Fib<N-1>::Result + Fib<N-2>::Result }; }; // 完全特化版 template <> struct Fib<1> { enum { Result = 1 }; }; // 完全特化版 template <> struct Fib<0> { enum { Result = 0 }; }; int main() { int i = Fib<10>::Result; // std::cout << i << std::endl; }
从这里可以看出,主模板带的参数是一个整型的数,而不是typename,得益于这一点,C++才有模板元编程,而其它高级语言只有泛型编程。
这里我有一个需求。在3D游戏编程中,常常需要二维、三维、四维的矢量,以便各种各样的数学运算。比方说需要求出两个向量确定平面的法向量,这一点在微软D3D中的d3dx9math.h中有很多体现,此外还需要适配基本的格式,像OpenGL一样,既提供float型的也提供double型的。
怎么实现呢?的确,用模板很好实现:
#ifndef MATH3D_H #define MATH3D_H // 以下代码需要高级版本C++编译器支持 template <typename T, int n> union Tuple { T m[n]; }; // 特化 template <typename T> union Tuple<T, 2> { Tuple( void ){ } Tuple( T _1, T _2 ) { Set( _1, _2 ); } void Set( T _1, T _2 ) { m[0] = _1, m[1] = _2; } operator T*( void ) // 重载类型转换函数 { return m; } Tuple operator+( const Tuple& obj ) const// 重载减号 { Tuple ret; ret.m[0] = m[0] + obj.m[0]; ret.m[1] = m[1] + obj.m[1]; return ret; } Tuple operator-( const Tuple& obj ) const// 重载减号 { Tuple ret; ret.m[0] = m[0] - obj.m[0]; ret.m[1] = m[1] - obj.m[1]; return ret; } Tuple& operator+=( const Tuple& obj ) { m[0] += obj.m[0]; m[1] += obj.m[1]; return *this; } //------------------------------------- T m[2]; struct { T x, y; }; struct { T w, h; }; }; template <typename T> union Tuple<T, 3> { Tuple( void ){ } Tuple( T _1, T _2, T _3 ) { Set( _1, _2, _3 ); } void Set( T _1, T _2, T _3 ) { m[0] = _1, m[1] = _2, m[2] = _3; } operator T*( void ) // 重载类型转换函数 { return m; } Tuple operator+( const Tuple& obj ) const// 重载减号 { Tuple ret; ret.m[0] = m[0] + obj.m[0]; ret.m[1] = m[1] + obj.m[1]; ret.m[2] = m[2] + obj.m[2]; return ret; } Tuple operator-( const Tuple& obj ) const// 重载减号 { Tuple ret; ret.m[0] = m[0] - obj.m[0]; ret.m[1] = m[1] - obj.m[1]; ret.m[2] = m[2] - obj.m[2]; return ret; } Tuple operator*( const T& obj ) const// 重载减号 { Tuple ret; ret.m[0] = m[0] * obj; ret.m[1] = m[1] * obj; ret.m[2] = m[2] * obj; return ret; } Tuple operator-( void ) { return Tuple( -m[0], -m[1], -m[2] ); } Tuple& operator+=( const Tuple& obj ) { m[0] += obj.m[0]; m[1] += obj.m[1]; m[2] += obj.m[2]; return *this; } //------------------------------------- T m[3]; struct { T x, y, z; }; struct { T r, g, b; }; }; template <typename T> union Tuple<T, 4> { Tuple( void ){ } Tuple( T _1, T _2, T _3, T _4 ) { Set( _1, _2, _3, _4 ); } void Set( T _1, T _2, T _3, T _4 ) { m[0] = _1, m[1] = _2, m[2] = _3, m[3] = _4; } operator T*( void ) // 重载类型转换函数 { return m; } Tuple operator+( const Tuple& obj ) const// 重载减号 { Tuple ret; ret.m[0] = m[0] + obj.m[0]; ret.m[1] = m[1] + obj.m[1]; ret.m[2] = m[2] + obj.m[2]; ret.m[3] = m[3] + obj.m[3]; return ret; } Tuple operator-( const Tuple& obj ) const// 重载减号 { Tuple ret; ret.m[0] = m[0] - obj.m[0]; ret.m[1] = m[1] - obj.m[1]; ret.m[2] = m[2] - obj.m[2]; ret.m[3] = m[3] - obj.m[3]; return ret; } Tuple& operator+=( const Tuple& obj ) { m[0] += obj.m[0]; m[1] += obj.m[1]; m[2] += obj.m[2]; m[3] += obj.m[3]; return *this; } //------------------------------------- T m[4]; struct { T x, y, z, w; }; struct { T r, g, b, a; }; }; // 定义数据类型 typedef Tuple<float, 2> Vector2F, Point2F, Size2F; typedef Tuple<float, 3> Vector3F, Color3F, Point3F; typedef Tuple<float, 4> Vector4F, Color4F;
这里用Tuple表示一个通用的类模板,第一个参数表示类型,如float、double等,第二个参数表示成员的个数。我们通过特化2、3、4个成员个数的类模板来分别定义这些行为。最后定义了Vector2F等一些常见的类型。你们看,是不是比较好地达到了要求?
此外,这里使用到了union联合体的相关用法,在下一次我将向大家介绍union的妙用。