泛型编程之模板
1.函数模板
重要行:template<typename T, typename T1>
关键值class和typename含义相同,那么我们以后就使用typename即可。
一般情况下的格式:
template<模板参数列表>
返回值类型 函数名(函数参数)
模板参数列表的理解:
函数参数列表在运行时调用者用实参来初始化形参
而对于模板参数列表,编译器用实际的不同版本的模板实参,代替,对应的模板参数,来实例化不同版本的模板。
两种模板参数:
模板类型参数:(书上这样命名很奇怪,他对应的有非类型模板参数,所以我还是喜欢称呼他为类型模板参数),可以用来指定返回类型,函数参数类型,以及在函数体内使用。
非类型模板参数: 模板实参必须是常量表达式,编译器使用字面常量来代替他们。
注:
模板在被使用的时候编译器才会生成代码,即“实例化”后。所以模板的头文件一般同时包括模板的声明和定义。模板要被实例化,那么他的一切都应当是可见的,所有模板作用域内名字都应该找到他的声明。
如果模板函数内部部分实现不支持推导的类型,我们在实例化模板的时候才会报错,所以编译和链接阶段都可能报错,因为处于不同编译单元的函数可能在链接阶段才被真正使用,在编译阶段我们可以只看到声明。(不重要,反正都是报错0.0)
模板函数的函数参数不强制需要使用模板参数
// AI生成
#include <iostream> // 定义一个函数模板,接受一个类型参数 T 和一个整数参数 N // 含有非类型模板参数,使用的时候需要尖括号指定 template <typename T, int N> void printNTimes(const T& value) { for (int i = 0; i < N; ++i) { std::cout << value << " "; } std::cout << std::endl; } int main() { // 使用函数模板,类型参数为 int,非类型参数为 5 printNTimes<int, 5>(42); // 输出 "42 42 42 42 42 " // 使用函数模板,类型参数为 double,非类型参数为 3 printNTimes<double, 3>(3.14); // 输出 "3.14 3.14 3.14 " return 0; }
// AI生成 #include <iostream> template <typename T> void printNTimes(const T& value, int N) { for (int i = 0; i < N; ++i) { std::cout << value << " "; } std::cout << std::endl; } int main() { // 调用函数模板,不需要尖括号 printNTimes(42, 5); // 输出 "42 42 42 42 42 " // 调用函数模板,不需要尖括号 printNTimes(3.14, 3); // 输出 "3.14 3.14 3.14 " return 0; }
2.类模板
与函数模板不同的是,编译器不能为类模板推断模板参数类型,需要我们利用尖括号提供额外信息(ps:毕竟类不像函数在调用的时候有实参列表),这些额外信息称之为显式模板实参。
类模板成员函数的实例化
类模板的成员函数只在使用时才被实例化(包括static成员函数),这意味着只有在实际调用该成员函数时,编译器才会生成相应的代码。这种延迟实例化的机制有助于减少编译时间和代码大小。
而且这一特性使得即使某种类型不能完全符合模板操作要求,我们依然可以使用该类型实例化类,只不过我们不去使用他的“非法”成员。
// AI #include <iostream> template <typename T> class MyClass { public: void printValue(T value) { std::cout << "Value: " << value << std::endl; } }; int main() { MyClass<int> intInstance; intInstance.printValue(5); // 实例化 printValue<int>(int) MyClass<double> doubleInstance; doubleInstance.printValue(3.14); // 实例化 printValue<double>(double) return 0; }
注:在类模板的作用域内,我们可以直接类模板的类名字(官方称谓:模板名),比如,某些成员的返回值 MyClass getData() { return *this; } 不用写成MyClass<T>。
但是要注意的是类外成员定义中函数返回值类型不在类作用域中,函数体内部在类作用域中。
模板类型别名
单个模板实例的别名:typedef Blob<string> A;
类模板的别名: template<typename T> using test = std::pair<T, int>; 使用的时候 test<string>;
// 利用AI生成 #include <iostream> #include <vector> template <typename T> class Container { private: std::vector<T> data; // 内部使用 vector 模板存储元素,使用了模板参数T public: // 在类内部声明成员函数 void add(const T& value) { data.push_back(value); } void remove(int index) { if (index >= 0 && index < data.size()) { data.erase(data.begin() + index); } } void update(int index, const T& value) { if (index >= 0 && index < data.size()) { data[index] = value; } } T& get(int index) { return data[index]; } int getSize() const { return data.size(); } // 在类外部定义的成员函数,使用了类模板的类型参数 T void print(); }; // 在类外部定义的成员函数,使用了类模板的类型参数 T template <typename T> void Container<T>::print() { for (const auto& item : data) { std::cout << item << " "; } std::cout << std::endl; } int main() { // 使用 int 类型的类模板 Container<int> intContainer; intContainer.add(1); intContainer.add(2); intContainer.add(3); std::cout << "Size: " << intContainer.getSize() << std::endl; // 输出 "Size: 3" intContainer.print(); // 输出 "1 2 3" // 使用 double 类型的类模板 Container<double> doubleContainer; doubleContainer.add(3.14); doubleContainer.add(2.718); std::cout << "Size: " << doubleContainer.getSize() << std::endl; // 输出 "Size: 2" doubleContainer.print(); // 输出 "3.14 2.718" return 0; }