Author: XFFer_
1 概述
2 函数模版的定义
3 函数模版的使用
4 非类型模版参数
vector实际上就是一个模版,vector才是一个类
overloaded function(重载函数)实际上就是两个同名函数,但是形参列表有明显不同。
template<typename T>
T funcadd(T a, T b)
{
T add_result = a + b;
return add_result;
}
template
关键开头的,后边跟<>
,里面叫模版参数列表(模版实参)<>
里必须有一个模版参数,模版参数前typename/class
,模版参数列表里表示函数定义中用到的“类型”或“值”template
和正常调用函数相似,系统会实例化出推断出的形参类型对应的函数
模版函数可以是内联inline
(加在template的下一行)的,模版定义不会生成代码,只会在调用函数模版时,才会实例化出一个特定版本的函数。
用传统的类型名来指定非类型参数,当模版被实例化时,这些非类型模版参数的值必须是常量表达式,在编译时实例化。
template<int a, int b>
int func()
{
int result = a + b;
return result;
}
int answer = func<12, 13>(); //显式的指定模版参数--在尖括号中提供额外的信息
1 概述
2 类模版定义
3 类模版的成员函数
4 模版类名字的使用
5 非类型模版参数
用类模版实例化一个特定的类,需要在模版名后用
<>
提供额外的信息。实例化类模版需要包含全部信息,包括类成员函数的定义
template <typename 形参名1, typename 形参名2, ..., typename 形参名n>
class 类名
{
...
};
template<typename T>
class myvector
{
public:
typedef T* myiterator;
public:
myiterator mybegin(); //返回值是myiterator,其实就是指向容器内部对象的指针
myiterator myend();
void func();
myvector& operator=(const myvector&);
};
template<模版参数表>
开始,类名后要用<>
把模版参数列表里的参数名列出template<typename T>
void myvector<T>::func
{
...
}
template<typename T>
myvector<T>& myvector<T>::operator=(const myvector&)
{
return *this;
}
1 typename的使用场合
2 函数指针做其他函数的参数
3 函数模版趣味用法举例
4 默认模版参数
typedef T* iterator;
vector<int>::iterator iter;
这个typedef
出的就是类型成员。
#include
vector<int> contain;
for (vector<int>::iterator iter = contain.begin(); iter != contain.end(); iter++) {}
这里的contain是一个类对象;iterator是实例化vector得到的类中的类型成员;iter是一个指向int对象的指针,因为typedef *T iterator
,contain是一个储存整形数据的类对象,可以调用类成员函数begin()
、end()
,返回值也是指向整形变量的指针。
template<typename T>
typename myvector<T>::myiterator myvector<T>::begin() {}
模版实例出的类后跟作用域标识符::
默认被系统处理成成员,而myiterator在这里作为类型成员(返回值类型),必须使用typename声明。
template<typename T, typename F>
void func(const T& i, const t& j, F funcpoint)
{
funcpoint(i, j);
}
int mcfunc(int i, int j)
{
return i+j;
}
func(2, 4, mcfunc); //T->int,F->int (*) (int, int)的函数指针
1 普通类的成员函数模版
2 类模版的成员函数模版
3 模版显式实例化,模版声明
成员函数都可以是函数模版,成为“成员函数模版”,虚函数。
template <typename T>
class A {
public:
template <typename P>
void func(P& p);
};
template <typename T>
template <typename P>
void A<T>::func(P& p) {}
类模版的成员函数(普通成员函数/模版成员函数)只有为程序所用才会实例化。
Q:为了防止在多个.cpp文件中都实例化相同的类模版
A:C++11提出“显式实例化”
//“显式实例化”手段中的“实例化定义”
template A<float>;
//“显式实例化”手段中的“实例化声明”
extern template A<float>; //不会再在本文件中生成这个实例化版本
//目的是告诉编译器,在其他文件中已经有了该模版的实例化版本
1 using定义模版别名
2 显式指定模版参数
typedef std::map<std::string, int> map_s_i;
但是typedef
定义出的是固定的格式。
C++98中:
template <typedef M>
struct map_s
{
typedef map<string, M> map_c;
};
//很类似iterator定义了一个类型成员
map_s<int>::map_c mapl;
C++11中:
template <typename T>
using str_map = map<string, T>;
1 类模版特化
类模版全特化
类模版偏特化2 函数模版特化
函数模版全特化
函数模版偏特化
特化:对特殊的类型(类型模版参数)进行特殊的处理。
template <typename T, typename F>
struct test {
void functest() {
cout << "调用了泛型版本" << endl; }
};
//特化类模版
template <>
struct test<int, double> {
void functest() {
cout << "调用了特化版本" << endl; }
};
//特化成员函数
template <>
void test<double, float>::functest() {
cout << "调用了特化版本的成员函数" << endl;
}
//参数数量进行偏特化
template <typename T, typename F, typename L>
struct test {
... };
template <typename F>
struct test<double, F, int> {
... };
//模版参数范围上的特化版本
template <typename T>
struct test {
... };
template <typename T>
struct test<const T> {
...}; //const特化版本
template <typename T, typename P>
void func(T& tmprv, P& tmprc)
{
... }
template <>
void func(double& tmprv, int& tmprc)
{
... }
函数模版没有偏特化!
必须都有泛型模版,才可以定义特化模版。模版定义、实现都放在一个.h
文件中。
1 可变参函数模版
简单范例
参数包的展开2 可变参类模版
通过递归继承方式展开参数包
...
的位置很关键!
template <typename... T>
void myfunc(T... argc)
{
cout << sizeof...(T) << endl; //返回T...的类型数
cout << sizeof...(argc) << endl;
}
T中存放的是任意个不同类型,称作可变参类型; argc中存放着任意个形参,称作可变形参。
一个参数typename T
和一包参数typename... F
,这种可变参函数模版更容易展开!
//参数包用迭代方式展开
void func() {} //终止迭代
template <typename T, typename... F>
void func(const T& src, const F&... stv)
{
cout << src << endl;
func(stv...);
}
template <typename... Args> class myclass { }; //为了保证可变参模版定义成功
template<> class myclass<> { //特化空模版
public:
myclass() {
cout << "myclass<>::myclass()执行" << endl;
}
};
template <typename First, typename... Others>
class myclass<First, Others...> : private myclass<Others...> //偏特化
{
public:
myclass() : m_i(0)
{
cout << "myclass::myclass执行了" << ' ' << "this: " << this << endl;
}
First m_i;
};
int main
{
myclass<int, float, double> cls();
}
(Debug结果:感觉挺难的)
cls()
括号内的参数是根据构造函数中的参数确定的
继承于
;
继承于
;
继承于< >
。模版参数同样也是特化版本< >
是基类对象,控制台输出*“myclass<>::myclass()执行”;紧接着
->
->
都会输出"myclass:myclass执行了"*1 可变参类模版
通过递归组合方式展开参数包
通过tuple和递归调用展开参数包
总结2 模版模版参数
组合关系(复合关系):类A中包含B对象(即在类A定义内部定义一个类B的对象)。
template <typename... Args> class myclass {}; //保证递归组合方式能够成功复合
template <>
class myclass<>
{
public:
myclass() {
cout << "myclass<>::myclass()执行" << endl; }
};
template <typename First, typename... Others>
class myclass<First, Others...>
{
public:
myclass(First prao, Others... pano) : m_i(0), m_t(pano)
First m_i;
myclass<Others...> m_t;
};
实现思路:计数器从0开始,每处理一个参数,计数器+1,一直到把所有参数处理完;最后搞一个模版偏特化,作为递归调用结束。
#include
//mycount用于统计,从0开始,mymaxcount表示参数数量
template <int mycount, int maxmycount, typename... T>
class myclass {
public:
static void mysfunc(const tuple<T...>& t)
{
cout << "value= " << get<mycount>(t) << endl;
//get是tuple元组的用法get<整数(表示位置从0开始)>(tuple对象)
myclass<mycount + 1, maxmycount, T...>::mysfunc(t);
//递归调用,计数器+1,取下一个tuple位置中的值
}
};
//特化一个结束版本
template<int maxmycount, typename... T>
class myclass<maxmycount, maxmycount, T...> {
public:
static void mysfunc(const tuple<T...>& t)
{
}
};
template <typename... T>
void myfunc(const tuple<T...>& t)
{
myclass<0, sizeof...(T), T...>::mysfunc(t);
}
int main()
{
tuple<float, int ,double> mytuple(3.2f, 3, 5);
myfunc(mytuple);
}
(也挺难的能看懂就好)
sizeof...(T)
用于在模版中还要使用类模版的情况下!
这是一个模版参数,前面都叫类型模版参数,这个模版参数本身,又是一个模版。
第种写法(第二种直接放里面了)
template <
typename T, //类型模版参数
template<class> class Container //模版 模版参数
//template typename Container
>
class myclass
{
public:
T t_i;
Container<T> myc;
myclass()
{
for (i = 0; i <10; i++)
{
myc.push_back(i);
}
}
};
template<typename T>
using myvec = vector<T, allocator<T>>; //需要确定一个分配器,系统没能自己确定
myclass<int, myvec> mvectobj;
//这里是因为vector有第二个参数allocator分配器,所以需要自己用using手动定义一个
(理解)
template class/typename 名字
Container
时,第一个typename系统确定为T,第二个确定为class(类)using myvec = vector>
是固定写法