C++:模板初阶
引入:
模板的发明对于泛型编程起了很大的作用,因为在实践的过程中我们发现,很多不同类型的功能类似的函数,其实现逻辑是类似的。
1.函数模板
语法:
关键字:template
写法1:
template<class T>
写法2:
template<typename T>
如果有多个参数,就用逗号分隔
template<typename T1,typename T2>
模板参数定义的是类型,typename后面的名字随便取,可以是T1,T2,也可以是其它的。
使用:
template<class T>//把这一句加在函数前面就行,不能用';'分隔
T f(T a,T b)
{ return a+b;
}
T的类型不同,调用的是不同的函数。
f<int>(1,2);
f<double>(1.0,2.0);
所以,模板的作用其实是编译器用模板生成实例化函数,不过函数的模板可以不用显式实例化
f(1,2);
f(1.0,2.0);
如果类型不匹配,编译会报错
1. 模板参数数量不匹配
如果模板定义时只有一个类型参数,但在实例化模板时提供了两个类型参数,这会导致编译错误。例如:
template <typename T>
void print(T value) {std::cout << value << std::endl;
}int main() {print<int, double>(10); // 错误:模板定义只有一个类型参数,但这里提供了两个return 0;
}
编译器会报错,因为print
模板只接受一个类型参数,而这里却传入了两个。
2. 模板参数类型不匹配
如果模板定义时的类型参数与实际传入的类型不匹配,也可能导致编译错误。不过,这种情况通常发生在模板函数的参数类型与模板参数不一致时。例如:
template <typename T>
void add(T a, T b) {std::cout << a + b << std::endl;
}int main() {add(10, 20.5); // 错误:模板参数类型不匹配return 0;
}
这里,add模板函数的两个参数类型必须相同,但传入的10是int类型,而20.5
是double
类型,因此编译器会报错。
解决方案:
add(10, (int)20.5);
//或者这样
add((double)10, 20.5);
3. 模板特化时类型不匹配
如果对模板进行了特化,但特化的类型与实际使用的类型不匹配,也会导致编译错误。例如:
template <typename T>
void print(T value) {std::cout << value << std::endl;
}template <>
void print<int>(int value) {std::cout << "Specialized for int: " << value << std::endl;
}int main() {print<double>(3.14); // 错误:特化只针对int,而这里使用了doublereturn 0;
}
逻辑相似的函数与函数模板可以同时存在吗?
template<class T>
T f(T a,T b)
{ return a+b;
}int f(int a,int b)
{return a+b;
}
事实上是可以的,在C++中,模板函数和普通函数可以同时存在,即使它们的签名在某些情况下完全一致,也不会导致编译错误。原因在于C++的函数重载规则和模板实例化的机制。
1.普通函数的优先级比模板函数要高
2.模板函数只有在被调用时才会被实例化。在实例化之前,编译器不会检查模板函数和普通函数之间的冲突。
3.如果模板函数实例化后与普通函数完全相同,模板函数会被“覆盖”,其实就是普通函数的优先级大于模板函数
2.类模板
template<class T1,class T2,...>
class 类模板名
{...
};
类模板必须显式实例化
类模板的声明和定义分离:
template<class T>
class 类模板名
{
public:f(T a){}
};
template<class T>//还要写
类模板名<T>::f(T a)
{...
}
普通的类:
类名就是类型,
类模板:
类型=类模板名<模板参数>
指定类域要用类型
不过类的声明和定义不要轻易分离,分离到两个文件里面会报错