C++ 模板是一种对类型进行参数化的工具,使用模板的目的就是能够让程序员编写与类型无关的代码。
模板分为 函数模板和类模板
函数模板的声明:
//template <typename type> ret-type func-name(parameter list)
//{
// 函数的主体
//}
template <typename T> T add(T t1, T t2)
{
return t1 + t2;
}
int main()
{
cout << add(1,2) << endl;//3
}
类模板的声明:
//template <class type> class class-name {
// ...
//}
template <typename T> class test_class
{
public:
T add_1(T t1, T t2)//类内实现
{
return t1 + t2;
}
T add_2(T t1, T t2);//类外实现
};
template<class T> T test_class<T>::add_2(T t1, T t2) {
return t1 + t2;
}
int main()
{
test_class<int> a;
cout << a.add(1,2) << endl;//3
}
模板的形参:
模板形参有三种类型:类型参数、非类型参数和模板类型参数。
1.类型参数
由class或者typename标记的参数,称为类型参数。类型参数是使用模板的主要目的, 参考上面代码。
那么class和typename,的关系和区别是什么呢,最后再说。
类型参数指定默认值
类模板的类型参数默认值和普通函数默认参数规则一致。
template<class T, typename M = double> void Show(T t, M m)
{
cout << t << "-" << m << endl;
return;
}
int main()
{
Show(1, "test");//1-test
Show<int, int>(1, 2.3);//1-2
Show<int>(1, 2.3);//1-2.3
Show(1, 2.3);//1-2.3
}
类型参数转换
template <typename T> T add(T t1, T t2)
{
return t1 + t2;
}
int main()
{
cout << add(1,2) << endl;//3
cout << add<int>(1,2.3) << endl;//3
cout << add<double>(1,2.3) << endl;//3.3
cout << add(1,2.3) << endl;//编译报错-未找到匹配的重载函数
}
2.非类型参数
非类型参数是指内置类型参数。例如,定义如下模板:
template <typename T, int len> class test_class
{
public:
int GetLen()
{
return sizeof(array);
}
private:
T array[len];
};
int main()
{
test_class<int, 5> a;
cout << a.GetLen() << endl;//20
test_class<double, 8> b;
cout << b.GetLen() << endl;//64
}
上述代码中int len就是非类型的模板参数。
使用非类型参数时,有以下几点需要注意。
(1)非类型参数只能是整型、字符型或枚举、指针、引用类型。
(2)调用非类型参数的实参必须是常量表达式,即必须能在编译时计算出结果。
(3)任何局部对象、局部变量的地址都不是常量表达式,不能用作非类型的实参,全局指针类型、全局变量也不是常量表达式,也不能用作非类型的实参。
(4)sizeof()表达式结果是一个常量表达式,可以用作非类型的实参。
(5)非类型参数一般不用于函数模板。
3.模板类型参数
模板类型参数就是模板的参数为另一个模板,声明格式如下所示:
//template <typename type_1, template<typename type_2, typename type_3> class type_class>
//class class-name {
// ...
//}
template<typename T> class ExampleAdd {
private:
T value;
public:
void setValue(T val_1, T val_2) {
value = val_1 + val_2;
};
T getValue() {
return value;
};
};
template<typename T> class ExampleRand {
private:
T value;
public:
void setValue(T val_1, T val_2) {
if (0 == rand() % 2)
{
value = val_1;
}
else
{
value = val_2;
}
};
T getValue() {
return value;
};
};
template <typename T, template<typename> class TC> class Example {
public:
void printValue(T val_1, T val_2) {
TC<T> instance;
instance.setValue(val_1, val_2);
std::cout << "Value: " << instance.getValue() << std::endl;
}
};
int main()
{
Example<int, ExampleAdd> Example_add;
Example_add.printValue(42, 11);//Value: 53
Example<std::string, ExampleRand> Example_rand;
Example_rand.printValue("test", "val");//Value: val
}
class和typename的区别:
class可以用来定义类,也可用作模板参数类型,而typename只能用作参数类型。
因为最初发明模板时决定使用class以减少一个关键字,但后来发现还是不得不加上typename关键字,原因是模板类型参数的声明格式要求必须要有关键字class。
除此之外在模板中使用没有区别。
关于模板工作原理:
模板定义并不是真正的定义了一个函数或者类,而是编译器根据程序员缩写的模板和形参来自己写出一个对应版本的定义,这个过程叫做模板实例化。
编译器生成的版本通常被称为模板的实例。编译器为程序员生成对应版本的具体过程。类似宏替换。
模板类在没有调用之前是不会生成代码的。
由于编译器并不会直接编译模板本身,所以模板的定义通常放在头文件中。