无锡网站制作哪家服务好,应付网站软件服务怎么做分录,软件开发平台培训,wordpress怎样修改原生登录按钮关于模板#xff0c;至少我们要先了解几个概念
一#xff1a;函数模板
二#xff1a;类模板
三#xff1a;模板特化
四#xff1a;形参参数包
模板覆盖的东西太多 我目前也不了解太多 函数模板 语法
templatetypename 类型名,typename 类型名,typename ...多参…
关于模板至少我们要先了解几个概念
一函数模板
二类模板
三模板特化
四形参参数包
模板覆盖的东西太多 我目前也不了解太多 函数模板 语法
templatetypename 类型名,typename 类型名,typename ...多参包类型名
//内部的typename可写多个 有时我们可能会看到 这里会写 class 意思大概是差不多的返回值 函数名(){};
如templatetypename arg, typename ...args
void set(arg s, args... d)
{val s;base::set(d...);
};
这里我们可以看到 templatetypename arg, typename ...args
模板形参类型 有 arg 和 args 形参包
arg 自然就代表可接受一个参数类型
而args 形参包也叫变参参数包 可接受多个参数
当然也有要求
一
模板形参必须可推导显示实例化也属于可推导
二
有显示实例化当然也有隐式实例化
但隐式实例化有个要求
函数参数中必须可推导模板形参
如
templatetypename arg, typename ...args
void set(arg s, args... d)
{val s;
};
模板形参在函数参数中直接用到了
可如此调用 这叫隐式实例化
set(10,g,50,100); //arg int args {char,int,int}这叫显示实例化
setint,char,int,int(10,g,50,100); //arg int args {char,int,int}
你会说 我们要是在函数参数中没一一使用到 模板形参怎么办
templatetypename Obj,typename arg, typename ...args
void set(arg s, args... d)
{Obj c(s,d...);
};
自然就要显示实例化了 注意必须可推导
setobj_2(10, 100);
// Obj obj_2 agr int args {int}
看就显示实例化了没用到的 Obj 模板形参 我们人看上去也是可推导的
编译器是人写的所以逻辑也是有些符合我们的思维的
函数特化有两种
偏特化部分特化
全特化
说实在的我也不太懂 我只能把我懂的部分 说一下
首先我们先看看 函数模板的偏特化 与 全特化
//主模版
templatetypename Obj,typename arg, typename ...args
void set(arg s, args... d)
{Obj c(s,d...);
};//特化版本 args* 形参包中 全为 指针类型
templatetypename Obj, typename arg, typename ...args
void set(arg* s, args*... d)
{Obj c(d..., s);
}//特化版本 Ret(*s)(a...) 非成员函数的函数指针
templatetypename Obj, typename Ret,typename ...a,typename ...args
void set(Ret(*s)(a...), args* ... d)
{Obj c((*d)..., s());
}//全特化
template
void setobj_2,float,float(float v, float d)
{obj_2 c(d, v);
}//全特化
template
void setobj_2(float v, float d,char k)
{obj_2 c(d, v);
}
这里有个叫主模版的函数模板 用特化就必须得需要主模版 什么叫主模板
我的理解中是 函数模板 最基本的哪个 比如上文
templatetypename Obj,typename arg, typename ...args void set(arg s, args... d) { Obj c(s,d...); };
这个模板可以容纳下面几个特化出来的函数参数的样子
比如指针类型 非成员函数的函数指针类型 成员函数的函数指针类型
也就是说它更加全面
你说都更加全面了 我为什么还有特化
这是为了 处理不同的情况嘛 就和函数重载时 处理不同情况一样
比如 int geti()
{return 50;
}int b 10, b2 100;
setobj_2(b,b2);
/*
调用的特化版本是
templatetypename Obj, typename arg, typename ...args
void set(arg* s, args*... d)
{Obj c(d..., s);
}
*/setobj_2(geti, b2);
/*
调用的特化版本是
templatetypename Obj, typename Ret,typename ...a,typename ...args
void set(Ret(*s)(a...), args* ... d)
{Obj c((*d)..., s());
}*/setobj_1(0.5f, 5.3f);
/*
调用主模板版本
templatetypename Obj,typename arg, typename ...args
void set(arg s, args... d)
{Obj c(s,d...);
};*/setobj_2(0.5f, 5.3f,p);
/*
调用的特化版本是
template
void setobj_2(float v, float d,char k)
{obj_2 c(d, v);
}*/setobj_2(0.f);
/*
调用主模版
templatetypename Obj,typename arg, typename ...args
void set(arg s, args... d)
{Obj c(s,d...);
};*/
有人可能看到 有些特化怎么 模板形参比主模版还多
templatetypename Obj, typename Ret,typename ...a,typename ...args
void set(Ret(*s)(a...), args* ... d)
{Obj c((*d)..., s());
}这就是模板特化的一部分特性 注意 特化版本的模板形参与主模版的模板形参并无瓜葛
就算他们是一样的名字
但是实则是有一定要求的
比如
//主模板
templatetypename Obj, typename arg
void set1(arg c) { Obj c{}; };//全特化
template
void set1obj_2,float(float c) { obj_2 bc{}; };//错误特化版本
template
void set1obj_2,int,char(int c) {};
/*这里我们注意到了, set1obj_2,int,char 有三个模板实参 而 我们的主模板只需要 两个模板实参 这就是要求 不能大于主模版要求的模板形参数目*///错误特化版本 与上述一样
template
void set1obj_2(int c,char b) {};
和函数重载 很类似的规则
有人会问了 那你第一种怎么可写好多个模板实参
注意主模版哦
templatetypename Obj,typename arg, typename ...args
void set(arg s, args... d)
{Obj c(s,d...);
};
一眼上去三个模板形参 但是我们最后是个模板形参包啊
不限个数的啊 超过两个的形参 统统进入形参包 这里可能有少年提出这样的写法
templatetypename arg, typename ...args
void setobj_2(arg s, args... d)
{obj_2 c(s,d...);
};
看着 嗯..... 我就特化处理 这个obj_2类型的
不过可惜 这样写法是错误的 注意右边的 编译输出错误
好了函数模板的特化说完了 ---------------------------------------------------
现在我们来看看类模板 上模板
//主模版
templatetypename tp
struct t1
{
};//特化版本
templatetypename Ret,typename Clss,typename ...Args
struct t1 Ret(Clss::*)(Args...)
{using F Ret(Clss::*)(Args...);t1(F fptr):ptr(fptr) {};F ptr;
};int main()
{t1decltype(obj_2::gets) b(obj_2::gets);return 0;
}
和函数模板特化差不多
类模板可通过构造函数的参数推断模板形参 templatetypename tp
struct t1
{using type tp;t1(tp p) :tpo(p) {};tp tpo;
};templatetypename Ret,typename Clss,typename ...Args
struct t1 Ret(Clss::*)(Args...)
{using F Ret(Clss::*)(Args...);t1(F fptr):ptr(fptr) {};F ptr;
};int main()
{t1decltype(obj_2::gets) b(obj_2::gets);t1 b t1(obj_2::gets);//特化版本 struct t1 Ret(Clss::*)(Args...)// F int(obj_2::*)(int,int,char);t1(obj_2()); // 推断的是主模版 tp obj_2return 0;
}
类模板可以弥补我们之前函数模板的遗憾
template typename tp
struct t2obj_2,tp
{t2(tp p):op(p) {}tp op;obj_2 d;};它可以这样特化
但是又有可惜的事情了 t1 b3 t1(obj_2()); //ok
auto c t2obj_2,decltype(b3)(b3); //ok template typename tp struct t2obj_2,tpauto c2 t2obj_2(b3); // error 可惜不可以这样调用 至少我是能看出来 应该可以推导
templatetypename obj,typename tp
struct t2
{using type tp;t2(obj* oj,tp p) :ptr(oj), tpo(p) {};obj* ptr;tp tpo;
};//main 中t1 b t1(obj_2::gets);
t1 b3 t1(obj_2());
auto c3 t2(b, b3);//可以
对了 忘记说一件事了
形参包 我们通过特化给它拆开
//主模版
templatetypename ...T
struct Tuple_text {};//特化
template
struct Tuple_text
{void set() {};
};//特化
templatetypename Ty1, typename ...Ty2
struct Tuple_textTy1, Ty2... :public Tuple_textTy2... {Ty1 val;using base Tuple_textTy2...;Tuple_text() {}templatetypename arg, typename ...argsTuple_text(arg a, args... d) :val(a), base(d ...) {}templatetypename ...argvoid set(arg... args) {};templatevoid set() {};templatetypename arg, typename ...argsvoid set(arg s, args... d){val s;base::set(d...);};base get(){return *this;}
};这是今天学习到的 c元组的类似做法
这里最关键的地方就是
template
struct Tuple_text
{void set() {};
};templatetypename Ty1, typename ...Ty2
struct Tuple_textTy1, Ty2... :public Tuple_textTy2...
{Ty1 val;
using base Tuple_textTy2...;Tuple_text() {}
templatetypename arg, typename ...args
Tuple_text(arg a, args... d) :val(a), base(d ...) {}}
没想到吧 我们的构造函数都能模板
这个有点复杂
我们展开看看 C Insights (cppinsights.io) 这个网站可以展开模板
#include cstdio//主模板
templatetypename ... T
struct Tuple_text
{
};//特化
template
struct Tuple_text
{inline void set(){}};//以下都为特化 实例化后其实也就是特化
template
struct Tuple_textlong : public Tuple_text
{long val;using base Tuple_text;inline Tuple_text();templateinline Tuple_textlong(long a): Tuple_text(), val{a}{}};template
struct Tuple_textfloat, long : public Tuple_textlong
{float val;using base Tuple_textlong;inline Tuple_text();templateinline Tuple_textfloat, long(float a, long __d1): Tuple_textlong(__d1), val{a}{}};template
struct Tuple_textchar, float, long : public Tuple_textfloat, long
{char val;using base Tuple_textfloat, long;inline Tuple_text();templateinline Tuple_textchar, float, long(char a, float __d1, long __d2): Tuple_textfloat, long(__d1, __d2), val{a}{}};template
struct Tuple_textint, char, float, long : public Tuple_textchar, float, long
{int val;using base Tuple_textchar, float, long;inline Tuple_text();templateinline Tuple_textint, char, float, long(int a, char __d1, float __d2, long __d3): Tuple_textchar, float, long(__d1, __d2, __d3), val{a}{}};int main()
{Tuple_textint, char, float, long c Tuple_textint, char, float, long(100, o, 6.0F, 500L);return 0;
}注意main 函数里
我们看到这是一系列的继承关系
我们去vs 看看内存布局 我们看到 Tuple_textint, char, float, long 类里面有所有的 val
那我们应该怎么拿到呢
Tuple_textint, char, float, long 的 val 很简单
但是 继承的 父类 Tuple_textchar, float, long 的 val
怎么拿呢
我们要是能转换为 父类对象就好了
using base Tuple_textTy2...;
base get()
{return *this;
}
这样是不是就能拿到父类对象了
Tuple_textint, char, float, long Tuple_textchar, float, long
Tuple_textfloat, long : Tuple_textlong : Tuple_text
每一级的 base 都是本级继承的父类
c.val c.get().val c.get().get().val c.get().get().get().val Tuple_text 这个是我们自己特化的类
是一个空的 所以继承链到此终结
模板编程是面向编译器的
很强大 但是也很难以解读
模板的玩法不只这些 玩法很多很多 看大家积累了 我也需要积累