c++之说_12|模板

关于模板,至少我们要先了解几个概念

一:函数模板

二:类模板

三:模板特化

四:形参参数包

模板覆盖的东西太多  我目前也不了解太多

函数模板 语法

template<typename 类型名,typename 类型名,typename ...多参包类型名> 
//内部的typename可写多个 有时我们可能会看到 这里会写 class 意思大概是差不多的
 返回值 函数名(){};
如

template<typename arg, typename ...args>
void set(arg s, args... d)
{
	val = s;
	base::set(d...);
};

这里我们可以看到  template<typename arg, typename ...args>

模板形参类型 有 arg  和  args 形参包 

arg  自然就代表可接受一个参数类型,

而args 形参包也叫变参参数包 可接受多个参数

当然也有要求

一:

模板形参必须可推导,显示实例化也属于可推导

二:

有显示实例化当然也有隐式实例化

但隐式实例化有个要求

函数参数中必须可推导模板形参

template<typename arg, typename ...args>
void set(arg s, args... d)
{
	val = s;
};

模板形参在函数参数中直接用到了

可如此调用  这叫隐式实例化
set(10,'g',50,100); //arg = int   args = {char,int,int}

这叫显示实例化
set<int,char,int,int>(10,'g',50,100); //arg = int   args = {char,int,int}

你会说 我们要是在函数参数中没一一使用到 模板形参怎么办?

template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
	Obj c(s,d...);
};

自然就要显示实例化了  注意必须可推导

set<obj_2>(10, 100);
// Obj = obj_2   agr = int  args ={int}

看就显示实例化了没用到的 Obj 模板形参  我们人看上去也是可推导的 

编译器是人写的所以逻辑也是有些符合我们的思维的

函数特化有两种

偏特化(部分特化)

全特化

说实在的我也不太懂  我只能把我懂的部分 说一下

首先我们先看看 函数模板的偏特化 与 全特化

//主模版
template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
	Obj c(s,d...);
};

//特化版本   args* 形参包中 全为 指针类型
template<typename Obj, typename arg, typename ...args>
void set(arg* s, args*... d)
{
	Obj c(d..., s);
}

//特化版本  Ret(*s)(a...)  非成员函数的函数指针
template<typename Obj, typename Ret,typename ...a,typename ...args >
void set(Ret(*s)(a...), args* ... d)
{
	Obj c((*d)..., s());
}

//全特化   
template<>
void set<obj_2,float,float>(float v, float d)
{
	obj_2 c(d, v);
}

//全特化
template<>
void set<obj_2>(float v, float d,char k)
{
	obj_2 c(d, v);
}

这里有个叫主模版的函数模板   用特化就必须得需要主模版  什么叫主模板?

我的理解中是  函数模板 最基本的哪个

 比如上文

template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
    Obj c(s,d...);
};

这个模板可以容纳下面几个特化出来的函数参数的样子 

比如指针类型  非成员函数的函数指针类型  成员函数的函数指针类型 

也就是说它更加全面

你说都更加全面了 我为什么还有特化

这是为了  处理不同的情况嘛   就和函数重载时  处理不同情况一样

比如


int geti()
{
	return 50;
}


int b = 10, b2 = 100;
set<obj_2>(&b,&b2);
/*
调用的特化版本是
template<typename Obj, typename arg, typename ...args>
void set(arg* s, args*... d)
{
	Obj c(d..., s);
}
*/


set<obj_2>(&geti, &b2);
/*
调用的特化版本是
template<typename Obj, typename Ret,typename ...a,typename ...args >
void set(Ret(*s)(a...), args* ... d)
{
	Obj c((*d)..., s());
}

*/

set<obj_1>(0.5f, 5.3f);
/*
调用主模板版本
template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
	Obj c(s,d...);
};

*/

set<obj_2>(0.5f, 5.3f,'p');
/*
调用的特化版本是
template<>
void set<obj_2>(float v, float d,char k)
{
	obj_2 c(d, v);
}


*/

set<obj_2>(0.f);
/*
调用主模版
template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
	Obj c(s,d...);
};

*/

有人可能看到 有些特化怎么 模板形参比主模版还多

template<typename Obj, typename Ret,typename ...a,typename ...args >
void set(Ret(*s)(a...), args* ... d)
{
	Obj c((*d)..., s());
}

这就是模板特化的一部分特性  注意 特化版本的模板形参与主模版的模板形参并无瓜葛 

就算他们是一样的名字 

但是实则是有一定要求的

比如

//主模板
template<typename Obj, typename arg>
void set1(arg c) { Obj c{}; };

//全特化
template<>
void set1<obj_2,float>(float c) { obj_2 bc{}; };

//错误特化版本  
template<>
void set1<obj_2,int,char>(int c) {};
/*
    这里我们注意到了, set1<obj_2,int,char>  有三个模板实参 
    而 我们的主模板只需要 两个模板实参  
    这就是要求:  
    不能大于主模版要求的模板形参数目

*/

//错误特化版本   与上述一样   
template<>
void set1<obj_2>(int c,char b) {};

和函数重载  很类似的规则

有人会问了 那你第一种怎么可写好多个模板实参?

注意主模版哦

template<typename Obj,typename arg, typename ...args>
void set(arg s, args... d)
{
	Obj c(s,d...);
};

一眼上去三个模板形参   但是我们最后是个模板形参包啊

不限个数的啊  超过两个的形参  统统进入形参包

这里可能有少年提出这样的写法

template<typename arg, typename ...args>
void set<obj_2>(arg s, args... d)
{
	obj_2 c(s,d...);
};

看着 嗯.....  我就特化处理  这个obj_2类型的 

不过可惜 这样写法是错误的

注意右边的 编译输出错误

好了函数模板的特化说完了

---------------------------------------------------

现在我们来看看类模板

      

上模板

//主模版
template<typename tp>
struct t1
{
};

//特化版本
template<typename Ret,typename Clss,typename ...Args>
struct t1 <Ret(Clss::*)(Args...)>
{
	using F = Ret(Clss::*)(Args...);
	t1(F fptr):ptr(fptr) {};

	F ptr;
};



int main()
{
	t1<decltype(&obj_2::gets)> b(&obj_2::gets);

	return 0;
}

和函数模板特化差不多

类模板可通过构造函数的参数推断模板形参

template<typename tp>
struct t1
{
	using type = tp;
	t1(tp p) :tpo(p) {};

	tp tpo;
};

template<typename Ret,typename Clss,typename ...Args>
struct t1 <Ret(Clss::*)(Args...)>
{
	using F = Ret(Clss::*)(Args...);
	t1(F fptr):ptr(fptr) {};

	F ptr;
};

int main()
{
	t1<decltype(&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_2
	return 0;
}

类模板可以弥补我们之前函数模板的遗憾

template< typename tp>
struct t2<obj_2,tp>
{
	t2(tp p):op(p) {}
	tp op;
	obj_2 d;

};

它可以这样特化

但是又有可惜的事情了


t1 b3 = t1(obj_2()); //ok
auto c =    t2<obj_2,decltype(b3)>(b3); //ok  template< typename tp> struct t2<obj_2,tp>

auto c2 = t2<obj_2>(b3); // error  可惜不可以这样调用  至少我是能看出来 应该可以推导
template<typename 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);//可以

对了  忘记说一件事了

形参包 我们通过特化给它拆开

//主模版
template<typename ...T>
struct Tuple_text {};

//特化
template<>
struct Tuple_text<>
{
	void set() {};
};

//特化
template<typename Ty1, typename ...Ty2>
struct Tuple_text<Ty1, Ty2...> :public Tuple_text<Ty2...> {
	Ty1 val;
	using base = Tuple_text<Ty2...>;
	

	Tuple_text() {}
	
    template<typename arg, typename ...args>
	Tuple_text(arg a, args... d) :val(a), base(d ...) {}
	
	template<typename ...arg>
	void set(arg... args) {};
	template<>
	void set<>() {};

	template<typename arg, typename ...args>
	void set(arg s, args... d)
	{
		val = s;
		base::set(d...);
	};

	base& get()
	{
		return *this;
	}
};

这是今天学习到的  c++元组的类似做法

这里最关键的地方就是

template<>
struct Tuple_text<>
{
	void set() {};
};

template<typename Ty1, typename ...Ty2>
struct Tuple_text<Ty1, Ty2...> :public Tuple_text<Ty2...>
{

Ty1 val;
using base = Tuple_text<Ty2...>;


Tuple_text() {}
template<typename arg, typename ...args>
Tuple_text(arg a, args... d) :val(a), base(d ...) {}

}

没想到吧  我们的构造函数都能模板

这个有点复杂

我们展开看看   C++ Insights (cppinsights.io)  这个网站可以展开模板

#include <cstdio>

//主模板
template<typename ... T>
struct Tuple_text
{
};

//特化
template<>
struct Tuple_text<>
{
  inline void set()
  {
  }
  
};


//以下都为特化  实例化后其实也就是特化
template<>
struct Tuple_text<long> : public Tuple_text<>
{
  long val;
  using base = Tuple_text<>;
  inline Tuple_text();
  
  template<>
  inline Tuple_text<long>(long a)
  : Tuple_text<>()
  , val{a}
  {
  }
  
};


template<>
struct Tuple_text<float, long> : public Tuple_text<long>
{
  float val;
  using base = Tuple_text<long>;
  inline Tuple_text();
  
  template<>
  inline Tuple_text<float, long>(float a, long __d1)
  : Tuple_text<long>(__d1)
  , val{a}
  {
  }
  
};


template<>
struct Tuple_text<char, float, long> : public Tuple_text<float, long>
{
  char val;
  using base = Tuple_text<float, long>;
  inline Tuple_text();

  template<>
  inline Tuple_text<char, float, long>(char a, float __d1, long __d2)
  : Tuple_text<float, long>(__d1, __d2)
  , val{a}
  {
  }
  
  
};


template<>
struct Tuple_text<int, char, float, long> : public Tuple_text<char, float, long>
{
  int val;
  using base = Tuple_text<char, float, long>;
  inline Tuple_text();
  
  template<>
  inline Tuple_text<int, char, float, long>(int a, char __d1, float __d2, long __d3)
  : Tuple_text<char, float, long>(__d1, __d2, __d3)
  , val{a}
  {
  }
  
};



int main()
{
  Tuple_text<int, char, float, long> c = Tuple_text<int, char, float, long>(100, 'o', 6.0F, 500L);
  return 0;
}

注意main 函数里

我们看到这是一系列的继承关系

我们去vs 看看内存布局

我们看到  Tuple_text<int, char, float, long> 类里面有所有的 val 

那我们应该怎么拿到呢?

Tuple_text<int, char, float, long> 的 val 很简单

但是 继承的 父类 Tuple_text<char, float, long> 的 val

怎么拿呢?

我们要是能转换为 父类对象就好了

using base = Tuple_text<Ty2...>;

base& get()
{
	return *this;
}

这样是不是就能拿到父类对象了?

Tuple_text<int, char, float, long>  : Tuple_text<char, float, long> :

Tuple_text<float, long> : Tuple_text<long> : Tuple_text<>

每一级的 base 都是本级继承的父类

c.val   c.get().val  c.get().get().val  c.get().get().get().val

Tuple_text<> 这个是我们自己特化的类

是一个空的 所以继承链到此终结

模板编程是面向编译器的

很强大 但是也很难以解读

模板的玩法不只这些  玩法很多很多 看大家积累了 我也需要积累

  • 15
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值