c++之说_12|模板

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

一:函数模板

二:类模板

三:模板特化

四:形参参数包

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

c++之说_12|模板_第1张图片

函数模板 语法

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

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

这里我们可以看到  template

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

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

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

当然也有要求

一:

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

二:

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

但隐式实例化有个要求

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

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

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

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

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

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

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

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

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

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

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

函数特化有两种

偏特化(部分特化)

全特化

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

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

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

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

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

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

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

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

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

 比如上文

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

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

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

也就是说它更加全面

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

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

比如


int geti()
{
	return 50;
}


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


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

*/

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

*/

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


*/

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

*/

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

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

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

就算他们是一样的名字 

但是实则是有一定要求的

比如

//主模板
template
void set1(arg c) { Obj c{}; };

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

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

*/

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

和函数重载  很类似的规则

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

注意主模版哦

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

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

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

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

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

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

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

c++之说_12|模板_第2张图片

注意右边的 编译输出错误

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

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

现在我们来看看类模板

c++之说_12|模板_第3张图片

      

上模板

//主模版
template
struct t1
{
};

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

	F ptr;
};



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

	return 0;
}

和函数模板特化差不多

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

c++之说_12|模板_第4张图片

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

	tp tpo;
};

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

	F ptr;
};

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

	t1 b = t1(&obj_2::gets);//特化版本 struct t1 
    // F = int(obj_2::*)(int,int,char);

	t1(obj_2()); // 推断的是主模版  tp = obj_2
	return 0;
}

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

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

};

它可以这样特化

但是又有可惜的事情了


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

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

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

//特化
template
struct Tuple_text :public Tuple_text {
	Ty1 val;
	using base = Tuple_text;
	

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

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

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

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

这里最关键的地方就是

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

template
struct Tuple_text :public Tuple_text
{

Ty1 val;
using base = Tuple_text;


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

}

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

这个有点复杂

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

#include 

//主模板
template
struct Tuple_text
{
};

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


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


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


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

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


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



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

注意main 函数里

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

我们去vs 看看内存布局

c++之说_12|模板_第5张图片

我们看到  Tuple_text 类里面有所有的 val 

那我们应该怎么拿到呢?

Tuple_text 的 val 很简单

但是 继承的 父类 Tuple_text 的 val

怎么拿呢?

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

using base = Tuple_text;

base& get()
{
	return *this;
}

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

Tuple_text  : Tuple_text

Tuple_text : Tuple_text : Tuple_text<>

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

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

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

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

模板编程是面向编译器的

很强大 但是也很难以解读

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

你可能感兴趣的:(C++,c++,开发语言)