C++ 模板
C++ 模板
Section titled “C++ 模板”模板是 C++ 的一个基本特性,实现了 泛型编程。它们允许你编写可以操作不同数据类型的代码(函数或类),而无需为每种类型重写代码。
可以将模板视为一个蓝图。你只需定义一次结构或算法,使用占位符类型名称。当你将模板用于特定类型(如 int 或 std::string)时,编译器会根据蓝图生成该特定类型的实际代码。
C++ 标准库大量依赖于模板。诸如 std::vector、std::list 和 std::map 之类的容器,以及 std::sort 和 std::find 之类的算法,都是基于模板的。这使得你可以使用相同的底层 std::vector 模板代码来创建 std::vector<int>、std::vector<double> 或 std::vector<MyClass>。
函数模板定义了一个可以操作各种数据类型的泛型函数。其语法通常如下所示:
template <typename T> // 或 template <class T>return_type function_name(parameter_list) { // 函数体使用类型 T}在这里,typename T(或 class T)将 T 声明为模板参数,一个类型的占位符。你可以在函数签名和函数体中使用 T,就好像它是一个特定的类型一样。
示例:一个用于查找两个值中最大值的函数模板。
#include <iostream>#include <string>
// 定义一个名为 'maximum' 的函数模板// 它接受两个类型为 T 的常量引用,并返回一个类型为 T 的常量引用template <typename T>const T& maximum(const T& a, const T& b) { // 假设类型 T 支持小于运算符 (<) return (a < b) ? b : a;}
int main() { int i = 39; int j = 20; // 编译器生成 maximum<int>(const int&, const int&) std::cout << "Max(i, j): " << maximum(i, j) << std::endl;
double f1 = 13.5; double f2 = 20.7; // 编译器生成 maximum<double>(const double&, const double&) std::cout << "Max(f1, f2): " << maximum(f1, f2) << std::endl;
std::string s1 = "Hello"; std::string s2 = "World"; // 编译器生成 maximum<std::string>(const std::string&, const std::string&) std::cout << "Max(s1, s2): " << maximum(s1, s2) << std::endl;
// 你也可以显式指定类型: // std::cout << "Max(i, j): " << maximum<int>(i, j) << std::endl;
return 0;}输出:
Max(i, j): 39Max(f1, f2): 20.7Max(s1, s2): World编译器从传递给 maximum 的参数推断出类型 T。对参数使用 const T& 是高效的,因为它避免了复制潜在的大对象。
类模板允许你定义泛型类。其语法类似于函数模板:
template <typename T1, typename T2, ...> // 或 class T1, class T2, ...class ClassName { // 使用 T1, T2 等类型的类成员private: T1 member1; T2 member2;public: ClassName(T1 val1, T2 val2); void someMethod(T1 arg); // ...};在这里,T1、T2 等是占位符类型,在创建类对象时指定。
示例:一个泛型 Stack 类模板。
#include <iostream>#include <vector>#include <stdexcept> // 用于 std::out_of_range#include <string>#include <utility> // 用于 std::move
template <typename T>class Stack {private: std::vector<T> elems; // 内部存储使用 std::vector
public: // 通过复制压入元素 void push(const T& elem) { elems.push_back(elem); }
// 通过移动压入元素 (C++11) void push(T&& elem) { elems.push_back(std::move(elem)); // 对于可移动类型高效 }
// 弹出(移除)顶部元素 void pop() { if (empty()) { throw std::out_of_range("Stack<>::pop(): empty stack"); } elems.pop_back(); }
// 返回顶部元素的常量引用 const T& top() const { if (empty()) { throw std::out_of_range("Stack<>::top(): empty stack"); } return elems.back(); }
// 返回顶部元素的可变引用 T& top() { if (empty()) { throw std::out_of_range("Stack<>::top(): empty stack"); } return elems.back(); }
// 检查栈是否为空 [[nodiscard]] bool empty() const noexcept { // C++11 noexcept, C++17 nodiscard return elems.empty(); }
// 获取栈当前大小 [[nodiscard]] size_t size() const noexcept { return elems.size(); }};
int main() { try { Stack<int> intStack; // 整数栈 Stack<std::string> stringStack; // 字符串栈
// 操作整数栈 std::cout << "Pushing 7 onto intStack..." << std::endl; intStack.push(7); std::cout << "Top element: " << intStack.top() << std::endl; std::cout << "Pushing 15 onto intStack..." << std::endl; intStack.push(15); std::cout << "Top element: " << intStack.top() << std::endl; std::cout << "Popping intStack..." << std::endl; intStack.pop(); std::cout << "Top element: " << intStack.top() << std::endl;
// 操作字符串栈 std::cout << "\nPushing \"hello\" onto stringStack..." << std::endl; stringStack.push("hello"); std::cout << "Top element: " << stringStack.top() << std::endl; std::string world = "world"; std::cout << "Pushing \"world\" onto stringStack..." << std::endl; stringStack.push(std::move(world)); // 移动语义示例 std::cout << "Top element: " << stringStack.top() << std::endl; std::cout << "Is 'world' string empty now? " << (world.empty() ? "Yes" : "No") << std::endl;
std::cout << "Popping stringStack twice..." << std::endl; stringStack.pop(); stringStack.pop();
// 这将抛出异常 std::cout << "Attempting to pop empty stringStack..." << std::endl; stringStack.pop();
} catch (const std::out_of_range& ex) { std::cerr << "\nCaught Exception: " << ex.what() << std::endl; return 1; // 指示错误 } catch (const std::exception& ex) { std::cerr << "\nCaught general exception: " << ex.what() << std::endl; return 1; }
return 0;}示例输出:
Pushing 7 onto intStack...Top element: 7Pushing 15 onto intStack...Top element: 15Popping intStack...Top element: 7
Pushing "hello" onto stringStack...Top element: helloPushing "world" onto stringStack...Top element: worldIs 'world' string empty now? YesPopping stringStack twice...Attempting to pop empty stringStack...
Caught Exception: Stack<>::pop(): empty stack注意:当在类定义之外定义类模板的成员函数时,你需要重复模板声明,并使用作用域解析运算符和模板参数(例如,template <typename T> void Stack<T>::pop() { ... })。
高级模板特性:
Section titled “高级模板特性:”现代 C++ 引入了强大的模板特性,例如:
- 变长参数模板(C++11): 允许模板接受可变数量的参数。
- 折叠表达式(C++17): 提供了一种简洁的语法,用于将运算符应用于参数包(来自变长参数模板)的所有元素。
- 类模板参数推导 (CTAD)(C++17): 在许多情况下,允许编译器从构造函数参数推导类模板的模板参数(例如,写
std::pair p(1, "hello");而不是std::pair<int, const char*> p(1, "hello);`)。 - Concepts(C++20): 允许指定对模板参数的约束,改善错误消息,并基于要求实现模板重载。