C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE

本来想把scanr,foldr什么的都写了的,一想太麻烦了,就算了,模板元编程差不多也该结束了,离开学还有10天,之前几天部门还要纳新什么的,写不了几天代码了,所以赶紧把这个结束掉,明天继续抄轮子叔的Win32库去。

逻辑结构和递归说白了就是做了一个If,一个For_N,If就和Excel里的If一样,For_N是把一个模板结构迭代N遍,为了所谓的方便,把If做成了宏,写起来还挺有意思的

 1 template<typename TTest, typename TTrue, typename TFalse>
 2 struct If;
 3
 4 template<typename TTrue, typename TFalse>
 5 struct If<Bool<true>, TTrue, TFalse>
 6 {
 7     using Result = TTrue;
 8 };
 9
10 template<typename TTrue, typename TFalse>
11 struct If<Bool<false>, TTrue, TFalse>
12 {
13     using Result = TFalse;
14 };
15
16 template<typename TTimes, template<typename TResult> typename TProc, typename TDefVal>
17 struct FOR_N;
18
19 template<template<typename TResult> typename TProc, typename TDefVal, int VTimes>
20 struct FOR_N<Int<VTimes>, TProc, TDefVal>
21 {
22     using Result = typename TProc<
23         typename FOR_N<
24             Int<VTimes - 1>,
25             TProc,
26             TDefVal
27         >::Result
28     >::Result;
29 };
30
31 template<template<typename TResult> typename TProc, typename TDefVal>
32 struct FOR_N<Int<0>, TProc, TDefVal>
33 {
34     using Result = typename TDefVal;
35 };
36
37 #if defined SIMPLIFIED_TML
38
39 #define IF If<
40 #define THEN ,
41 #define ELSE ,
42 #define NOELSE , void
43 #define NOTHEN , void
44 #define ENDIF >::Result
45 #define ELIF ELSE IF
46
47 #endif

用法就不给了,不会写单元测试

然后是列表的又一部分零碎的东西,Filter就是按照给定的一元谓词筛选整个列表,MapNone和FilterNone属于什么也不做/直接返回true的默认操作,ListGeneration是从给定的列表中生成列表,就像haskell的列表推导式一样,说白了就是Filter一下再Map一下,组合起来挺有意思的,Range就是给定一个类型和上下界,生成一个连续的区间,这里突然想到还可以自定义步长,懒得改了就这样。

 1 template<template<typename TArg> typename TFilter, typename T>
 2 struct ListFilter;
 3
 4 template<template<typename TArg> typename TFilter, typename... TArgs>
 5 struct ListFilter<TFilter, List<TArgs...>>
 6 {
 7     using Param = List<TArgs...>;
 8     using Result = typename ListConnect<
 9         typename If<
10             typename TFilter<typename Param::First>::Result,
11             List<typename Param::First>,
12             List<>
13         >::Result,
14         typename ListFilter<
15             TFilter,
16             typename Param::Rest
17         >::Result
18     >::Result;
19 };
20
21 template<template<typename TArg> typename TFilter, typename TLast>
22 struct ListFilter<TFilter, List<TLast>>
23 {
24     using Param = List<TLast>;
25     using Result = typename If<
26         typename TFilter<typename Param::First>::Result,
27         List<typename Param::First>,
28         List<>
29     >::Result;
30 };
31
32 template<typename T>
33 struct MapNone
34 {
35     using Result = T;
36 };
37
38 template<typename T>
39 struct FilterNone
40 {
41     using Result = Bool<true>;
42 };
43
44 template<template<typename TVal> typename TProcToVal, template<typename TVal> typename TFilterProc, typename... TSeq>
45 struct ListGeneration;
46
47 template<template<typename TVal> typename TProcToVal, template<typename TVal> typename TFilterProc, typename... TSeq>
48 struct ListGeneration<TProcToVal, TFilterProc, List<TSeq...>>
49 {
50     using FilterResult = typename ListFilter<TFilterProc, List<TSeq...>>::Result;
51     using Result = typename ListMap<TProcToVal, FilterResult>::Result;
52 };
53
54 template<typename TType, TType VBegin, TType VEnd, TType VCurr = VBegin>
55 struct Range
56 {
57     using Result = typename ListConnect<
58         List<PODType<TType, VCurr>>,
59         typename If<
60             Bool<VCurr < VEnd>,
61             typename Range<TType, VBegin, VEnd, VCurr + 1>::Result,
62             List<>
63         >::Result
64     >::Result;
65 };

用起来还算很爽

1 ListGeneration<Mul2, FilterNone, Range<int, 0, 63>::Result>::Result list2;

Mul2是一个定义好的操作,就是接受一个类型值,然后把他乘以2,C++模板的递归层数最多不能超过255原来是指整个表达式的递归层数啊,我这里上界最多写到63就不能写下去了。

SFINAE,模板替换失败不是一个错误,在编译器根据模板寻找重载函数的时候,如果一个重载的模板替换失败,那么编译器会去寻找其他的重载,直到最后找不到了再报错,由此可以通过一组函数重载过一个过滤器,根据不同的模板参数执行做不同的返回值,通过返回值来达到类型筛选和查错的目的。

C++17的Concept也是基于SFINAE的,好激动。

只看了type SFINAE,expression的还没看,也不准备看了,感觉没什么卵用,还是过段时间买那本C++模板元编程系统学习一下吧。

这里翻译了一下cppreference.com上的东西:

以下属于SFINAE错误

  • 尝试实例化一个包含多个不同长度包的包扩展(没看懂,这是什么意思)
  • 尝试创建一个void数组,引用的数组,函数的数组,抽象类的数组,负数或零长度的数组
  • 尝试在::运算符左侧使用一个非类或枚举类型
  • 尝试使用一个类型的成员,若:尝试创建一个指向引用的指针
    • 该类型不包含此成员
    • 在需要类型的地方,这个成员不是类型
    • 在需要模板的地方,这个成员不是模板类型
    • 在需要非类型的地方,这个成员不是非类型
  • 尝试创建一个void引用
  • 尝试创建一个指向T的成员的指针,但T不是类类型
  • 尝试给非类型模板参数一个错误类型的值
  • 尝试在以下情形进行一个无效转换:尝试创建一个参数是void类型的函数
    • 在模板参数表达式中
    • 在函数声明表达式中
  • 尝试创建一个返回函数或数组的函数
  • 尝试创建一个const volatile修饰的函数
  • 尝试创建一个参数或返回值是抽象类的函数

自己也没写什么东西,对这套路子不是很熟,等我把C++那些奇怪的语法和指针声明什么的弄明白了这里也就该差不多了。

时间: 2024-08-22 21:01:25

C++模板元编程 - 3 逻辑结构,递归,一点列表的零碎,一点SFINAE的相关文章

C++模板元编程 - 2 模仿haskell的列表以及相关操作

这是昨天和今天写的东西,利用C++的可变模板参数包以及包展开,模式匹配的一些东西做的,感觉用typename...比轮子叔那个List<A,List<B, List<C, D>>>的设计要好看不少. List有一个很巧妙的继承,只有那么做才能使用类似于List<>::Rest的写法,直接定义成template<typename T, typename... TArgs>List是不行的. Change这里可以给一个args...换另一个包装,这里

模板元编程加速递归

//模板元编程加速递归 //缺点:模板元在编译时处理,无法调试,并且会加大代码体积 #include<iostream> using namespace std; template <int N> struct data { enum{res = data<N - 1>::res + data<N - 2>::res}; }; template <>//模板具体化 struct data<1> { enum{res = 1}; };

【C/C++学院】0816-引用包装器/仿函数/转义字符 R”()”/using别名/模板元编程 比递归优化/智能指针/多线程/静态断言以及调试技能的要求 assert

引用包装器  std::ref(变量) #include<iostream> template<class T> void com(T arg)//模板函数,引用无效,引用包装器 { std::cout <<"com ="<< &arg << "\n"; arg++; } void main() { int count = 10; int & rcount = count; com(coun

C++拾遗--模板元编程

C++拾遗--模板元编程 前言 模板元是用于递归加速的,把运行期的函数调用变到编译期进行代码展开,类似于内联函数.下面看一个实例:斐波那契数列第n项求解. 模板元编程 #include <iostream> #include <ctime> using namespace std; //递归法 int fib(int n) { if (n < 0) return 0; if (n == 1 || n == 2) return 1; return fib(n - 1) + fi

模板元编程

//模板元把运行时消耗的时间,在编译期间优化 //递归极其消耗时间 1 #include <iostream> 2 3 //模板元把运行时消耗的时间,在编译期间优化 4 //递归极其消耗时间 5 6 template <int N> 7 struct data 8 { 9 enum { res = data<N - 1>::res + data<N - 2>::res }; 10 }; 11 12 template <> 13 struct da

使用模板元编程操作类型集合(C++11下的TypeList)

Wrote by mutouyun. (http://darkc.at/cxx-type-list/) 群里有个朋友要实现这么一个功能:如何在编译期把一个函数类型的参数减少一个. 简单来说,就是实现下面这个模板: remove_func_par<2, void(int, long, short)>::type; // type = void(int, long) 根据输入的编译期整数,把函数参数表里对应的参数干掉一个. 为了实现这种功能,我们需要操作变参模板的参数包.比如像这样: // mak

读书笔记 effective c++ Item 48 了解模板元编程

1. TMP是什么? 模板元编程(template metaprogramming TMP)是实现基于模板的C++程序的过程,它能够在编译期执行.你可以想一想:一个模板元程序是用C++实现的并且可以在C++编译器内部运行的一个程序,它的输出——从模板中实例化出来的C++源码片段——会像往常一样被编译. 2. 使用TMP的优势 如果这没有冲击到你,是因为你没有足够尽力去想. C++不是为了模板元编程而设计的,但是自从TMP早在1990年被发现之后,它就被证明是非常有用的,为了使TMP的使用更加容易

Item 48:了解模板元编程

Item 48: Be aware of template metaprogramming. 模板元编程(Template Metaprogramming,TMP)就是利用模板来编写那些在编译时运行的C++程序. 模板元程序(Template Metaprogram)是由C++写成的,运行在编译器中的程序.当程序运行结束后,它的输出仍然会正常地编译. C++并不是为模板元编程设计的,但自90年代以来,模板元编程的用处逐渐地被世人所发现. 模板编程提供的很多便利在面向对象编程中很难实现: 程序的工

c++模板元编程二:用enum做数值计算

2.1 用enum做数值计算 下面两篇文章都介绍了模板元编程,enum是其最重要的基本工具 http://www.codeproject.com/Articles/3743/A-gentle-introduction-to-Template-Metaprogramming https://www10.informatik.uni-erlangen.de/~pflaum/pflaum/ProSeminar/meta-art.html 因此可以得道以下结论: enum的值由编译器在编译期计算 利用模