注:博客中内容主要来自《狄泰软件学院》,博客仅当私人笔记使用。
测试环境:Ubuntu 10.10
GCC版本:9.2.0
一、多参数类模板
1)类模板可以定义任意多个不同的类型参数
函数模板也可以支持多个不同类型参数。
使用类模板必须一一指定类型。
2)类模板可以被特化
- 指定类模板的特定实现
- 部分类型参数必须显示指定
- 根据类型参数分开实现类模板
特殊化为只接受一个类型(T1和T2类型相同)。在指定实际类型时,如果T1和T2类型相同,编译器会优先使用右边的模板。编译器会根据实际情况分析使用左边或者右边。编译器认为这两个模板是同一个,在使用的时候会根据参数进行选择使用。
3)类模板的特化类型
- 部分特化 - 用特定规则约束类型参数
- 完全特化 - 完全显示指定类型参数
完全特化里没有泛指类型。 如果编译器指明T1、T2使用int,编译器会使用完全特化。
编程实验
类模板的特化
59-1.cpp
#include <iostream>
#include <string>
using namespace std;
template
< typename T1, typename T2 >
class Test //多参数类模板
{
public:
void add(T1 a, T2 b)
{
cout << "void add(T1 a, T2 b)" << endl;
cout << a + b << endl;
}
};
int main()
{
Test<int, float> t1;
t1.add(1, 2.5); //3.5
return 0;
}
操作:
1) 使用模板:g++ 59-1.cpp -o 59-1.out编译正确,打印结果:
void add(T1 a, T2 b)
3.5
分析:
使用模板时必须显示声明模板参数类型。
2) 使用模板特化:
#include <iostream>
#include <string>
using namespace std;
template
< typename T1, typename T2 >
class Test //多参数类模板
{
public:
void add(T1 a, T2 b)
{
cout << "void add(T1 a, T2 b)" << endl;
cout << a + b << endl;
}
};
//模板特化(部分特化:加了约束调价T1和T2相同)
template
< typename T >
class Test < T, T > //当Test类模板的两个类型参数完全相同时,使用这个实现
{
public:
void add(T a, T b)
{
cout << "void add(T a, T b)" << endl;
cout << a + b << endl;
}
};
int main()
{
Test<int, float> t1;
Test<long, long> t2;
t1.add(1, 2.5); //3.5
t2.add(5, 5);
return 0;
}
g++ 59-1.cpp -o 59-1.out编译正确,打印结果:
void add(T1 a, T2 b)
3.5
void add(T a, T b)
10
小实验:
#include <iostream>
#include <string>
using namespace std;
template
< typename T1, typename T2 >
class Test //多参数类模板
{
public:
void add(T1 a, T2 b)
{
cout << "void add(T1 a, T2 b)" << endl;
cout << a + b << endl;
}
};
//模板特化(部分特化:加了约束调价T1和T2相同)
template
< typename T >
class Test < T, T > //当Test类模板的两个类型参数完全相同时,使用这个实现
{
public:
void add(T a, T b)
{
cout << "void add(T a, T b)" << endl;
cout << a + b << endl;
}
void print()
{
cout << "class Test < T, T >" << endl;
}
};
int main()
{
Test<int, float> t1;
Test<long, long> t2;
t1.add(1, 2.5); //3.5
t2.add(5, 5);
t2.print();
return 0;
}
g++ 59-1.cpp -o 59-1.out编译正确,打印结果:
void add(T1 a, T2 b)
3.5
void add(T a, T b)
10
class Test < T, T >
3) 使用完全特化:
#include <iostream>
#include <string>
using namespace std;
template
< typename T1, typename T2 >
class Test //多参数类模板
{
public:
void add(T1 a, T2 b)
{
cout << "void add(T1 a, T2 b)" << endl;
cout << a + b << endl;
}
};
//模板特化(部分特化:加了约束调价T1和T2相同)
template
< typename T >
class Test < T, T > //当Test类模板的两个类型参数完全相同时,使用这个实现
{
public:
void add(T a, T b)
{
cout << "void add(T a, T b)" << endl;
cout << a + b << endl;
}
void print()
{
cout << "class Test < T, T >" << endl;
}
};
//完全特化
template //当 T1 == void* 并且 T2 == void*时
< >
class Test < void*, void* >
{
public:
void add(void* a, void* b)
{
cout << "void add(void* a, void *b)" << endl;
cout << "Error to add void* param..." << endl;
}
};
int main()
{
Test<int, float> t1;
Test<long, long> t2;
Test<void*, void*> t3;
t1.add(1, 2.5); //3.5
t2.add(5, 5);
t2.print();
t3.add(NULL, NULL);
Test<int*, double*> t4;
int a = 1;
double b = 0.1;
t4.add(&a, &b);
return 0;
}
g++ 59-1.cpp -o 59-1.out编译错误:
59-1.cpp: In instantiation of ‘void Test<T1, T2>::add(T1, T2) [with T1 = int*; T2 = double*]’:
在实例'void Test<T1, T2>::add(T1, T2) [T1 = int*; T2 = double*]':
59-1.cpp:76:15: required from here
59-1.cpp:14:13: error: invalid operands of types ‘int*’ and ‘double*’ to binary ‘operator+’
cout << a + b << endl;
错误:类型'int*'和'double*'相加是非法操作
解决办法:定义一个类模板,用于指针类型相加
#include <iostream>
#include <string>
using namespace std;
template
< typename T1, typename T2 >
class Test //多参数类模板
{
public:
void add(T1 a, T2 b)
{
cout << "void add(T1 a, T2 b)" << endl;
cout << a + b << endl;
}
};
template
< typename T1, typename T2 >
class Test < T1*, T2* > //关于指针的特化实现
{
public:
void add(T1* a, T2* b)
{
cout << "void add(T1 a, T2 b)" << endl;
cout << *a + *b << endl;
}
};
//模板特化(部分特化:加了约束调价T1和T2相同)
template
< typename T >
class Test < T, T > //当Test类模板的两个类型参数完全相同时,使用这个实现
{
public:
void add(T a, T b)
{
cout << "void add(T a, T b)" << endl;
cout << a + b << endl;
}
void print()
{
cout << "class Test < T, T >" << endl;
}
};
//完全特化
template //当 T1 == void* 并且 T2 == void*时
< >
class Test < void*, void* >
{
public:
void add(void* a, void* b)
{
cout << "void add(void* a, void *b)" << endl;
cout << "Error to add void* param..." << endl;
}
};
int main()
{
Test<int, float> t1;
Test<long, long> t2;
Test<void*, void*> t3;
t1.add(1, 2.5); //3.5
t2.add(5, 5);
t2.print();
t3.add(NULL, NULL);
Test<int*, double*> t4;
int a = 1;
double b = 0.1;
t4.add(&a, &b);
return 0;
}
g++ 59-1.cpp -o 59-1.out编译正确,打印结果:
void add(T1 a, T2 b)
3.5
void add(T a, T b)
10
class Test <T, T>
void add(void* a, void* b)
Error to add void* param...
void add(T1* a, T2* b)
1.1
4)类模板特化注意事项
- 特化只是模板的分开实现
* 本质上是同一个类模板
- 特化类模板的使用方式是统一的
* 必须显示指定每一个类型参数
二、问题
类模板特化与重定义有区别吗?
函数模板可以特化吗?
三、特化的深度分析
1)重定义和特化的不同
- 重定义
* 一个类模板和一个新类(或者两个类模板)
* 使用的时候需要考虑如何选择的问题
- 特化
* 以统一的方式使用类模板和特化类(只有一个类模板)
* 编译器自动优先选择特化类
2)函数模板只支持类型参数完全特化
template
< typename T > //函数模板定义
bool Equal(T a, T b)
{
return a == b;
}
template
< > //函数模板完全特化
bool Equal<void*>(void* a, void* b)
{
return a == b;
}
测试:模板的重定义
#include <iostream>
#include <string>
using namespace std;
template
<typename T1, typename T2>
class Test
{
public:
void add(T1 a, T2 b)
{
cout << "void add(T1 a, T2 b)" << endl;
cout << a + b << endl;
}
};
template
<typename T1, typename T2>
class Test <T1*, T2*>
{
public:
void add(T1* a, T2* b)
{
cout << "void add(T1* a, T2* b)" << endl;
cout << *a + *b << endl;
}
};
template
<typename T>
class Test<T, T>
{
public:
void add(T a, T b)
{
cout << "void add(T a, T b)" << endl;
cout << a + b << endl;
}
void print()
{
cout << "class Test <T, T>" << endl;
}
};
/*
template
< >
class Test<void*, void*>
{
public:
void add(void* a, void* b)
{
cout << "void add(void* a, void* b)" << endl;
cout << "Error to add void* param..." << endl;
}
};
*/
class Test_Void
{
public:
void add(void* a, void* b)
{
cout << "void add(void* a, void* b)" << endl;
cout << "Error to add void* param..." << endl;
}
};
int main()
{
Test<int, float> t1;
Test<long, long> t2;
//Test<void*, void&> t3;
Test_Void t3; //采用了重定义模板,写代码会考虑用特化还是重定义,后期维护不好
t1.add(1, 2.5);
t2.add(5, 5);
t2.print();
t3.add(NULL, NULL);
Test<int*, double*> t4;
int a = 1;
double b = 0.1;
t4.add(&a, &b);
return 0;
}
注意:能用特化用特化,别混用。
编程实验
特化的深入理解
59-2.cpp
#include <iostream>
#include <string>
using namespace std;
template
< typename T >
bool Equal(T a, T b) //函数模板
{
cout << "bool Equal(T a, T b)" << endl;
return a == b; //不能用来比较两个浮点数相等。解决办法:函数模板特化
}
template //函数模板完全特化——显示声明
< >
bool Equal<double>(double a, double b) //浮点数精度不够。完全特化
{
const double delta = 0.00000000000001;
double r = a - b;
cout << "bool Equal<double>(double a, double b)" << endl;
return (-delta < r) && (r < delta); //用这种方式判断两个数相等
}
int main()
{
cout << Equal( 1, 1 ) << endl; //函数模板
cout << Equal<double>( 0.001, 0.001 ) << endl;
return 0;
}
操作:
1) g++ 59-2.cpp -o 59-1.out编译正常,打印结果:
bool Equal(T a, T b)
1
bool Equal<double>(double a, double b)
1
2) 定义重载函数:
#include <iostream>
#include <string>
using namespace std;
template
< typename T >
bool Equal(T a, T b) //函数模板
{
cout << "bool Equal(T a, T b)" << endl;
return a == b; //不能用来比较两个浮点数相等。解决办法:函数模板特化
}
template //函数模板完全特化——显示声明
< >
bool Equal<double>(double a, double b) //浮点数精度不够。完全特化
{
const double delta = 0.00000000000001;
double r = a - b;
cout << "bool Equal<double>(double a, double b)" << endl;
return (-delta < r) && (r < delta); //用这种方式判断两个数相等
}
bool Equal(double a, double b) //函数
{
const double delta = 0.00000000000001;
double r = a - b;
cout << "bool Equal(double a, double b)" << endl;
return (-delta < r) && (r < delta);
}
int main()
{
cout << Equal( 1, 1 ) << endl; //函数模板
cout << Equal( 0.001, 0.001 ) << endl;
return 0;
}
打印结果:
bool Equal(T a, T b)
1
bool Equal(double a, double b)
1
分析:编译器会优先选择重载的全局函数,放弃了特化。
改变编译器行为,让它使用模板特化:
int main()
{
cout << Equal( 1, 1 ) << endl; //函数模板
cout << Equal<>( 0.001, 0.001 ) << endl; //加上<>,让编译器先匹配特化
return 0;
}
打印结果:
bool Equal(T a, T b)
1
bool Equal<double>(double a, double b)
1
四、工程中的建议(编码原则)
当需要重载函数模板时,优先考虑实验模板特化;
当模板特化无法满足需求,再使用函数重载!
小结
1)类模板可以定义任意多个不同的类型参数
2)类模板可以被部分特化和完全特化
3)特化的本质是模板的分开实现
4)函数模板只支持完全特化
5)工程中使用模板特化代替类(函数)重定义