现在的位置: 首页 > 综合 > 正文

模板编程总结

2019年04月21日 ⁄ 综合 ⁄ 共 3399字 ⁄ 字号 评论关闭

1、template <typename T>//template parameter list
2、模版形参可以表示类型形参,也可以表示常量表达式的非类型形参。
3、使用函数模版时,编译器会推断出哪个或哪些模版实参绑定到模版形参。一旦编译器确定了实际的模版实参,就称它实例化了函数模版的一个实例。
4、inline 只能放在template<>之后
5、类型形参也存在屏蔽问题。模版形参的名字只能在同一个列表中出现一次,但可以在不同的列表中出现。
6、typename和class没有区别,只是在老的版本中可能只能用class,在模版内部定义制定类型只能用typename。
7、非类型模版形参,可以返回数组的大小。如template <typename T, size_t N> int get_len(T (&a[N]){return N;}
8、编写代码时,对实参类型的要求应尽可能少。两个原则:模版的形参是const引用;函数体内的测试只用<比较。
9、类模板的静态成员变量有两种初始化方法:
template<class T>
class A
{
public:
static int ab;
static T c;
};

template<class T>
int A<T>::ab = 0;
//
template<>
double A<double>::c = 0.0;

10、模板类和模板函数的编译模型有包含模型和分别编译模型。包含模型没有大问题,只是会产生多个实例,然后连接的时候,编译器挑一个,忽略其他,这个跟编译器有关;
分别编译的话,
函数模板的定义处,加export;
export template<class T>
int add(T a, T b){}

类模板:
在实现文件中:
export template<class T>
class AA;
#include"AA.h"
...
11、类模板尽量别用非类型参数,否则容易引起代码膨胀;非类型参数必须是编译时常量表达式。
12、类模板的友元类或者友元函数,可以是不带模板的,也可以是模板的,或者是模板+约束的;最后一种,模板+约束,必须在类之前声明一下。

template<class T>
ostream& operator <<(ostream &, A<T>&);

template <class T2>
class B;

template <class T1>
class A
{
friend ostream& operator << <T1>(ostream &, A<T1>&);  //前置声明
friend class B<T1>;   // 前置声明
friend class B<int>;  //不用前置声明
}

13、嵌套从属类型,要加typename。typename A<T>::value_type a = 1;但有两个地方不出现:一个是基类列表,一个是成员初始化列表
14、模板函数:模板实参推断,常规的数据类型转换都不用,只有两个:(1.非const -> const, 2. 参数不是引用的时候,数组->指针),当参数是引用时,数组不转为指针。
15、模板函数和普通函数重载,两个标准:1.不用参数转换的优先;2.普通函数优先;
16、模板函数的特化与重载的普通函数的区别:普通函数有类型转换,特化的只有模板实参推断;
17、类模板的operator *函数:声明为友元函数。理由:不应该成为成员函数,否则不能执行单构造函数的隐式转换(这种转换只发生在函数的参数上),另外,由于模板会阻碍
隐式转换,希望他不是模板函数,所以变成友元函数,放进函数声明里,这样,当整个类实例化的时候,这个函数也实例化了,调用的时候,就不再是模板了。
18、成员模板的例子:智能指针的构造函数,希望可以接受原生指针,weak_ptr的对象,等等,所以使用成员模板,这些构造函数是explicit的,而拷贝构造函数则不是explicit
但是,不能让谁来做参数都行,比如char*->double,不合理。所以:
template<class T>
class share_ptr{
template<class U>
shared_ptr(const share_ptr<U>&other):ptr(other.get_ptr()) //初始化列表进行初始化,如果本来不兼容,那么这里也不兼容
{

}
...
};
但还是要实现非泛化版本的拷贝构造函数
19、继承时,由于考虑到,模板类可以偏特化,所以,并不保证基类的东西肯定会存在,所以想要引用基类的成员,必须用以下三种方法之一:
using A<T>::fun1;
this->func1()
A<T>::fun1()
20、普通成员函数,定义在类外的话:
template<class T>
int AA<T>::func()
{
}

21、编写模板类,对实参类型的要求尽可能宽泛。

22、函数指针
template <class T>
int add(T a, T b){}

int (*f)(int, int) = add; // add在这里就实例化了

[1]特化,偏特化
[2]成员模板
[3]traits技巧
首先需要两个不同的类型,比如int和float,实现两种行为,定义两个重载版本。trait类实现一种多对1的映射,如
template<class T>
struct AA
{  typedef int type;
};
则默认类型为int,现在特化一个:
template<>
struct AA<float>
{ typedef double type;
};
则输入float可以得到double.

[4]继承
[5]表达式模板
[6]元编程
template <size_t T>
class A
{
enum{value = T * A<T-1>::value; };
};
template <>
class A<1>
{
enum{value = 1;};
};

[7]继承

#include <iostream>
using namespace std;

template<class T>
class A
{#include <iostream>
using namespace std;

template<class T>
class A
{
public:
static int ab;
static T c;
int count;
A(int i):count(i){}
public:
// template<class T>  加上这句话就编译不通过,因为成员函数模板不能是virtual的
virtual void print(){cout<<"A<T>"<<" "<<c<<endl;}
};
template <class T>
int A<T>::ab = 9;
template <>
double A<double>::c = 0.9;

template<class U>
class B:public A<U>
{
public:
B(int i):A<U>(i){}
void print()
{
cout<<"count= "<<count<<endl;
}
};

int main()
{
B<double> x(90);
cout<<x.ab<<endl;  //继承了静态变量
cout<<x.c<<endl;  //继承了静态变量
x.print();          //继承了共有函数
// x.prin();   //继承了共有成员变量

A<double>* p1 = new B<double>(800);
p1->print();

A<double>* p2 = &x;
p2 ->print();

return 0; 
}

成员模板函数不能是虚函数,因为编译的时候编译器就希望在解析这个类声明的时候就知道虚表的大小,
但是成员模板呢?显然要看整个程序中有多少个地方实例化了它,这样就必须等整个程序都解析以后才行。所以编译器规定成员模板不能为虚。

// 没有引用的数组,只有数组的引用 
void print(int (&a)[10])
{
int i;
for(i=0;i<10;i++)
cout<<a[i]<<" ";
cout<<endl;
}
此时数组不退化!
应该是编译阶段的一种check,汇编码就是直接push数组首地址。
可以搞成模板
template<class T, int n>
void print(T (&a)[n])
{
int i;
for(i=0;i<n;i++)
cout<<a[i]<<" ";
cout<<endl;
}
int main()
{

int b[] = {1,3,2,5,2,1,6,78,0,98};
double a[] = {3.4,2.5,2.7};
print(a);
print(b);
return 0; 
}

抱歉!评论已关闭.