C++ 模板元编程简单小栗子
最近看了看模板的元编程,感觉有点意思。
一些计算可以在编译过程就能够完成,榨干编译器的最后一点资源。
stl中用的全是这些玩意。
当然,这增加了编译时长。
我记得貌似有“图灵完备”这个说法——模板的元编程现在能够实现判断跳转、循环,理论上来说,一般能够用运行时解决的问题,在编译时都能够解决。
但这个好像没有什么意义。如果通篇都用这种代码来编写程序,那么这和直接嵌入汇编有什么区别?
我学这个纯属是为了让别人看不懂。
(我真的猜不透你耶……)
疯子就应该有疯子应该有的疯度。
写了几个小例子用来备忘。
学习参考链接: https://www.cnblogs.com/qicosmos/p/4480460.html
1.判断类型是否相同(C++自带也有 std::is_same<T1, T2>::value)
1 template <typename T1, typename T2> 2 struct is_same_type 3 { 4 enum { value = false }; 5 }; 6 7 template <typename T> 8 struct is_same_type<T, T> 9 { 10 enum { value = true }; 11 };
2.计算阶乘
1 //n! 2 template<int n> 3 struct factorial 4 { 5 private: 6 enum: unsigned long long { 7 tmp = n * factorial<n - 1>::value //It is useless. 8 }; 9 10 public: 11 enum { 12 value = tmp >= INT_MAX ? -1 : tmp //return n * factorial(n - 1); 13 }; 14 }; 15 16 template<> 17 struct factorial<0> //if(0 == n) 18 { 19 enum { value = 1 }; //return 1 20 };
3.获取一组数中最大的数
1 //get max number. 2 template<int n, int... ns> 3 struct max_num 4 { 5 enum {value = n}; //return n; 6 }; 7 8 template <int ln/*left number*/, int rn/*right number*/, int... ns/*numbers...*/> 9 struct max_num<ln, rn, ns...> 10 { 11 enum {value = 12 ln >= rn ? //if(ln >= rn) 13 max_num<ln, ns...>::value : //max_num(ln) 14 max_num<rn, ns...>::value // else max_num(rn) 15 }; 16 };
4.根据模板中不同的类型来获得不同的值(比如SocketTCP 和 SocketTCP 使用不同的协议类型和数据传输类型)
1 enum PROTO_TYPE 2 { 3 PROTO_NONE = 0, 4 PROTO1 = 1, 5 PROTO2 = 2 6 }; 7 8 class A 9 { 10 }; 11 12 class B 13 { 14 }; 15 16 template <class T> 17 struct getproto 18 { 19 enum { 20 value = //value = 21 std::is_same<T, A>::value ? PROTO1 : //if(typeT == typeA) return PROTO1; 22 std::is_same<T, B>::value ? PROTO2 : //else if(typeT == typeB) return PROTO2; 23 //TODO: others... 24 PROTO_NONE //else return PROTO_NONE; 25 }; 26 };
5. 编译时循环
前面的阶乘计算和寻找最大的数都用到了循环,本质是递归。他们类似于这样
1 template<int b_, int e_> 2 struct LOOP_CALL 3 { 4 LOOP_CALL() 5 { 6 LOOP_CALL<b_, e_ - 1>(); 7 // TODO: loop once, index is e_ - 1 8 // 9 } 10 }; 11 12 template<int b_> 13 struct LOOP_CALL<b_, b_> 14 { 15 LOOP_CALL(){ } 16 };
其中, 循环范围是 [b_, e_)。
但这里也存在着问题。由于它的本质是递归,那么编译器定会对这种递归的层数有上限设定。比如我在main函数中调用 LOOP_CALL<0, 1000>(); ,此时编译器报错(使用 mingw32-make 进行编译):
[build] C:\Users\user-20220927-001\Desktop\tt\cpp\test.h: In instantiation of 'LOOP_CALL<b_, e_>::LOOP_CALL() [with int b_ = 0; int e_ = 101]': [build] C:\Users\user-20220927-001\Desktop\tt\cpp\test.h:88:9: recursively required from 'LOOP_CALL<b_, e_>::LOOP_CALL() [with int b_ = 0; int e_ = 999]' [build] C:\Users\user-20220927-001\Desktop\tt\cpp\test.h:88:9: required from 'LOOP_CALL<b_, e_>::LOOP_CALL() [with int b_ = 0; int e_ = 1000]' [build] C:\Users\user-20220927-001\Desktop\tt\cpp\main.cpp:8:24: required from here [build] C:\Users\user-20220927-001\Desktop\tt\cpp\test.h:88:9: fatal error: template instantiation depth exceeds maximum of 900 (use -ftemplate-depth= to increase the maximum) [build] LOOP_CALL<b_, e_ - 1>(); [build] ^~~~~~~~~~~~~~~~~~~~~~~ [build] compilation terminated.
当递归层数到达900时,编译器就开始报错了,并提示用 -ftemplate-depth= 来更改设置。但如果不更改设置的情况下,如何解决这个问题?
我所想到的办法就是:既然不允许一次递归层数太多,那么我可以每一个调用递归100次,执行10次调用,就可以做到 LOOP_CALL<0, 1000>(); 的成功执行,类似于:
1 #define __Partition (100) 2 3 LOOP_CALL<0, __Partition>(); 4 LOOP_CALL<__Partition, __Partition * 2>(); 5 LOOP_CALL<__Partition * 2, __Partition * 3>(); 6 //......
这部分依然可以用递归去解决:
1 #define __Min (1) 2 #define __Max (1000) 3 #define __Partition (100) 4 5 #define __IsNotNeedSplit (__Max - __Min <= __Partition) 6 7 template<int b_, int e_, bool is_> 8 struct __RecursivePartition 9 { 10 }; 11 12 template<int b_, int e_> 13 struct __RecursivePartition<b_, e_, true> 14 { 15 __RecursivePartition() 16 { 17 LOOP_CALL<b_, e_>(); 18 } 19 }; 20 21 template<int b_, int e_> 22 struct __RecursivePartition<b_, e_, false> 23 { 24 enum 25 { 26 next_b = b_ + __Partition, 27 is_ = (e_ - next_b <= __Partition), 28 }; 29 30 __RecursivePartition() 31 { 32 LOOP_CALL<b_, b_ + __Partition>(); 33 __RecursivePartition<next_b, e_, is_>(); 34 } 35 };
然后在调用时:
__RecursivePartition<__Min, __Max, __IsNotNeedSplit>();
这样一个语句就可以了。
0.主函数用来测试
1 int main() 2 { 3 std::cout << "is_same_type: " <<is_same_type<int, int>::value << std::endl; 4 std::cout << "factorial:" << factorial<10>::value << std::endl; 5 std::cout << "max_num:"<< max_num<1, 2, 54, 2, 36, 4>::value << std::endl; 6 7 std::cout << "getproto: " << getproto<B>::value << std::endl; 8 9 system("pause>nul"); 10 return 0; 11 }