template关键字
在C++中,template关键字用于定义模板,它是实现泛型编程的核心。
模板基本语法
函数模板
#include <iostream>// 使用模板
template <typename T>
T my_max(T a, T b) {return (a > b) ? a : b;
}int main() {int a = 5, b = 10;double x = 3.14, y = 2.71;// 编译器自动推导 T 的类型std::cout << "my_max(a, b) = " << my_max(a, b) << std::endl; // T = intstd::cout << "my_max(x, y) = " << my_max(x, y) << std::endl; // T = double// 也可以显式指定类型std::cout << "my_max<double>(a, b) = " << my_max<double>(a, b) << std::endl; // T = doublereturn 0;
}
T是什么?
-
T 是一个模板类型参数,它是一个占位符
-
在编译时,编译器会根据调用时传入的参数类型自动推导 T 的具体类型
-
T 可以是任何支持 > 运算符的类型(int, double, string 等)
类模板
#include <iostream>
#include <vector>
#include <string>
#include <stdexcept> // 用于std::runtime_errortemplate <typename T>// T是类型参数,可以是任何数据类型
class Stack {
private:std::vector<T> elements;// 使用std::vector作为底层存储容器public:// 入栈操作void push(const T& value) {elements.push_back(value);// 将元素添加到vector末尾}// 出栈操作T pop() {if (elements.empty()) {throw std::runtime_error("Stack is empty"); // 栈空时抛出异常}T value = elements.back();// 获取栈顶元素elements.pop_back();//移除栈顶元素return value;//返回栈顶元素}// 检查栈是否为空bool empty() const {return elements.empty();}// 添加更多实用方法T top() const {if (elements.empty()) {throw std::runtime_error("Stack is empty");}return elements.back();}size_t size() const {return elements.size();}
};int main() {// 创建整数栈Stack<int> intStack;intStack.push(1);intStack.push(2);intStack.push(3);std::cout << "整数栈大小: " << intStack.size() << std::endl;std::cout << "栈顶元素: " << intStack.top() << std::endl;// 弹出所有元素while (!intStack.empty()) {std::cout << "弹出: " << intStack.pop() << std::endl;}// 创建字符串栈Stack<std::string> stringStack;stringStack.push("Hello");stringStack.push("World");stringStack.push("C++");std::cout << "\n字符串栈内容:" << std::endl;while (!stringStack.empty()) {std::cout << stringStack.pop() << std::endl;}return 0;
}
/*
输出结果:整数栈大小: 3
栈顶元素: 3
弹出: 3
弹出: 2
弹出: 1字符串栈内容:
C++
World
Hello*/
模板参数 T 的作用
T 是一个类型占位符,在实例化时会被具体类型替换:
-
Stack → T 变成 int
-
Stackstd::string → T 变成 std::string
-
Stack → T 变成 double
模板参数类型
类型参数(typename/class)
// 定义有两个类型参数的模板类
template <typename T1, class T2> // typename 和 class 在模板参数中完全等价
class Pair {
private:T1 first; // 第一个数据成员,类型为 T1T2 second; // 第二个数据成员,类型为 T2public:// 构造函数:用两个参数初始化 first 和 secondPair(T1 f, T2 s) : first(f), second(s) {}// 获取第一个元素的常量成员函数T1 getFirst() const { return first; }// 获取第二个元素的常量成员函数T2 getSecond() const { return second; }
};
// 使用
Pair<int, std::string> person(1, "Alice");
-
T1 和 T2 都是类型参数
-
typename 和 class 在模板参数中完全等价,可以互换使用
-
每个实例化的 Pair 类都会绑定到特定的 T1 和 T2 类型
完整示例
#include <iostream>
#include <string>template <typename T1, class T2>
class Pair {
private:T1 first;T2 second;public:Pair(T1 f, T2 s) : first(f), second(s) {}T1 getFirst() const { return first; }T2 getSecond() const { return second; }// 可以添加设置方法void setFirst(T1 f) { first = f; }void setSecond(T2 s) { second = s; }// 可以添加显示方法void display() const {std::cout << "(" << first << ", " << second << ")" << std::endl;}
};int main() {// 示例1:整数和字符串的PairPair<int, std::string> person(1, "Alice");std::cout << "ID: " << person.getFirst() << ", Name: " << person.getSecond() << std::endl;// 示例2:字符串和浮点数的PairPair<std::string, double> product("Apple", 2.99);std::cout << "Product: " << product.getFirst() << ", Price: $" << product.getSecond() << std::endl;// 示例3:相同类型的PairPair<double, double> coordinates(3.14, 2.71);std::cout << "Coordinates: (" << coordinates.getFirst() << ", " << coordinates.getSecond() << ")" << std::endl;// 示例4:使用显示方法Pair<int, std::string> student(101, "Bob");student.display();return 0;
}
/*
输出结果:
ID: 1, Name: Alice
Product: Apple, Price: $2.99
Coordinates: (3.14, 2.71)
(101, Bob)
*/
非类型模板参数
这是一个固定大小数组模板类,使用了非类型模板参数来指定数组大小。
// 模板参数:一个类型参数 T 和一个非类型参数 Size
template <typename T, int Size>
class FixedArray {
private:T data[Size]; // 固定大小的数组,大小在编译时确定public:// 重载下标运算符(非常量版本)T& operator[](int index) {return data[index]; // 返回元素的引用,允许修改}// 重载下标运算符(常量版本)const T& operator[](int index) const {return data[index]; // 返回常量引用,不允许修改}/*const 关键字表示这个函数不会修改对象状态用于常量对象的访问*/// 静态常量成员函数,返回数组大小static constexpr int size() { return Size; }
};
// 使用
FixedArray<double, 10> array; // 创建大小为10的double数组
模板默认参数
template <typename T = int, int Size = 100>
class Container {// 实现
};// 使用
Container<> container1; // 使用默认参数 T=int, Size=100
Container<double> container2; // T=double, Size=100
Container<double, 50> container3; // T=double, Size=50
模板特化*****
全特化
#include <iostream>
#include <string>// 主模板
template <typename T>
class Printer {
public:void print(const T& value) {std::cout << "Generic: " << value << std::endl;}
};// 全特化 - 为 std::string 提供特殊实现
template <>
class Printer<std::string> {
public:void print(const std::string& value) {std::cout << "String: \"" << value << "\"" << std::endl;}
};// 可以为更多类型提供特化
template <>
class Printer<double> {
public:void print(const double& value) {std::cout << "Double: " << std::fixed << value << std::endl;}
};// 为指针类型提供特化
template <typename T>
class Printer<T*> {
public:void print(T* value) {if (value) {std::cout << "Pointer: " << *value << std::endl;} else {std::cout << "Pointer: nullptr" << std::endl;}}
};int main() {// 使用主模板Printer<int> intPrinter;intPrinter.print(42); // 输出: Generic: 42// 使用 std::string 特化Printer<std::string> strPrinter;strPrinter.print("Hello"); // 输出: String: "Hello"// 使用 double 特化Printer<double> doublePrinter;doublePrinter.print(3.14159); // 输出: Double: 3.141590// 使用指针特化int x = 100;Printer<int*> ptrPrinter;ptrPrinter.print(&x); // 输出: Pointer: 100Printer<int*> nullPtrPrinter;nullPtrPrinter.print(nullptr); // 输出: Pointer: nullptrreturn 0;
}
-
针对具体类型的特化
-
完全重写类的实现
-
可以有不同的成员函数和数据成员
偏特化
// 主模板
template <typename T1, typename T2>
class MyClass {
public:void print() {std::cout << "Primary template" << std::endl;}
};
/*
接受两个任意类型参数
提供通用实现
*/// 偏特化 - 两个类型相同的情况
template <typename T>
class MyClass<T, T> {
public:void print() {std::cout << "Both types are the same" << std::endl;}
};
/*
当 T1 和 T2 是相同类型时使用
语法:MyClass<T, T>
*/// 偏特化 - 第二个类型为int的情况
template <typename T>
class MyClass<T, int> {
public:void print() {std::cout << "Second type is int" << std::endl;}
};
/*
当第二个模板参数是 int 时使用
语法:MyClass<T, int>
*/
// 使用
int main() {MyClass<int, double> obj1;obj1.print(); // Primary templateMyClass<int, int> obj2;obj2.print(); // Both types are the sameMyClass<double, int> obj3;obj3.print(); // Second type is intreturn 0;
}
完整示例
#include <iostream>// 主模板
template <typename T1, typename T2>
class MyClass {
public:void print() {std::cout << "Primary template" << std::endl;}
};// 偏特化 - 两个类型相同的情况
template <typename T>
class MyClass<T, T> {
public:void print() {std::cout << "Both types are the same" << std::endl;}
};// 偏特化 - 第二个类型为int的情况
template <typename T>
class MyClass<T, int> {
public:void print() {std::cout << "Second type is int" << std::endl;}
};// 偏特化 - 第一个类型为指针的情况
template <typename T1, typename T2>
class MyClass<T1*, T2> {
public:void print() {std::cout << "First type is a pointer" << std::endl;}
};int main() {// 使用主模板MyClass<int, double> obj1;obj1.print(); // Primary template// 使用第一个偏特化(类型相同)MyClass<int, int> obj2;obj2.print(); // Both types are the same// 使用第二个偏特化(第二个参数为int)MyClass<double, int> obj3;obj3.print(); // Second type is int// 使用第三个偏特化(第一个参数为指针)MyClass<int*, double> obj4;obj4.print(); // First type is a pointer// 测试优先级:同时匹配多个偏特化时选择最特化的MyClass<int*, int> obj5; // 匹配:指针特化和int特化obj5.print(); // 输出:First type is a pointer(指针特化更具体)return 0;
}
全特化和偏特化的区别
全特化(Full Specialization)
- 定义:为模板的所有参数提供具体的类型
- 语法:template<> class ClassName<SpecificTypes…>
// 主模板
template <typename T1, typename T2>
class Example {
public:void print() { std::cout << "Primary" << std::endl; }
};// 全特化 - 所有参数都具体指定
template <>
class Example<int, std::string> {
public:void print() { std::cout << "Full specialization" << std::endl; }
};// 使用
Example<int, std::string> obj; // 使用全特化版本
特点:
-
所有模板参数都被具体类型替换
-
是一个完全具体的类,不再是模板
-
可以完全重新设计实现,不依赖主模板
偏特化(Partial Specialization)
- 定义:只对部分模板参数提供具体类型或施加约束
- 语法:template<typename…> class ClassName<PartialTypes…>
// 主模板
template <typename T1, typename T2>
class Example {
public:void print() { std::cout << "Primary" << std::endl; }
};// 偏特化 - 第二个参数固定为int
template <typename T>
class Example<T, int> {
public:void print() { std::cout << "T and int" << std::endl; }
};// 偏特化 - 两个参数类型相同
template <typename T>
class Example<T, T> {
public:void print() { std::cout << "Same types" << std::endl; }
};// 使用
Example<double, int> obj1; // 使用 T and int 偏特化
Example<float, float> obj2; // 使用 Same types 偏特化
Example<double, std::string> obj3; // 使用主模板
特点:
-
仍然是模板,有未指定的模板参数
-
对参数施加模式约束(如类型相同、指针类型等)
-
必须遵循主模板的基本结构
对比表格

小结:
-
全特化:为特定类型组合提供完全定制的实现
-
偏特化:为一类类型模式提供特殊实现
-
选择规则:全特化 > 偏特化 > 主模板
-
偏特化更灵活:可以定义类型关系(相同、指针、引用等模式)
可变参数模板
基本用法
这是一个使用可变参数模板(Variadic Templates)和递归展开实现的通用打印函数,可以接受任意数量、任意类型的参数。
// 递归终止函数
void print() {std::cout << std::endl;
}
/*
当参数包为空时调用此函数
输出换行符并结束递归
*/// 可变参数模板函数
template <typename T, typename... Args>
void print(T first, Args... args) {std::cout << first << " ";print(args...); // 递归调用
}
/*
typename... Args:参数包,表示0个或多个类型
Args... args:函数参数包,表示0个或多个参数
递归调用:每次处理一个参数,剩余参数继续递归
*/
// 使用
int main() {print(1, 2.5, "hello", 'a'); // 输出: 1 2.5 hello areturn 0;
}
/*
// 第一次调用
print<int, double, const char*, char>(1, 2.5, "hello", 'a')
→ 输出: "1 "
→ 调用: print(2.5, "hello", 'a')// 第二次调用
print<double, const char*, char>(2.5, "hello", 'a')
→ 输出: "2.5 "
→ 调用: print("hello", 'a')// 第三次调用
print<const char*, char>("hello", 'a')
→ 输出: "hello "
→ 调用: print('a')// 第四次调用
print<char>('a')
→ 输出: "a "
→ 调用: print()// 第五次调用
print()
→ 输出: 换行符
→ 递归终止
*/
折叠表达式(C++17)
折叠表达式是 C++17 引入的新特性,它允许对参数包中的所有元素应用二元运算符,无需递归展开。
// C++17 折叠表达式
// 打印函数的折叠表达式版本
template <typename... Args>
void print(Args... args) {(std::cout << ... << args) << std::endl; // 一元右折叠
}
/*
展开过程;
// 调用 print(1, 2, 3) 展开为:
std::cout << 1 << 2 << 3 << std::endl;// 相当于:
((std::cout << 1) << 2) << 3 << std::endl;
*///求和函数的折叠表达式版本
template <typename... Args>
auto sum(Args... args) {return (args + ...); // 一元右折叠
}
/*调用 sum(1, 2, 3, 4, 5) 展开为:
1 + (2 + (3 + (4 + 5)));计算结果:15
*/// 使用
int main() {print(1, 2, 3); // 输出: 123std::cout << sum(1, 2, 3, 4, 5) << std::endl; // 输出: 15return 0;
}
折叠表达式的四种形式
- 一元右折叠 (args op …)
template <typename... Args>
auto right_fold(Args... args) {return (args + ...); // 展开为: arg1 + (arg2 + (arg3 + ...))
}
- 一元左折叠 (… op args)
template <typename... Args>
auto left_fold(Args... args) {return (... + args); // 展开为: ((arg1 + arg2) + arg3) + ...
}
- 二元右折叠 (args op … op init)
template <typename... Args>
auto binary_right_fold(Args... args) {return (args + ... + 0); // 展开为: arg1 + (arg2 + (arg3 + 0))
}
- 二元左折叠 (init op … op args)
template <typename... Args>
auto binary_left_fold(Args... args) {return (0 + ... + args); // 展开为: ((0 + arg1) + arg2) + arg3
}
完整示例
#include <iostream>// C++17 折叠表达式
template <typename... Args>
void print(Args... args) {(std::cout << ... << args) << std::endl; // 一元右折叠
}template <typename... Args>
auto sum(Args... args) {return (args + ...); // 一元右折叠
}// 更多折叠表达式示例
template <typename... Args>
auto product(Args... args) {return (args * ...); // 一元右折叠:乘法
}template <typename... Args>
bool all_true(Args... args) {return (args && ...); // 一元右折叠:逻辑与
}template <typename... Args>
bool any_true(Args... args) {return (args || ...); // 一元右折叠:逻辑或
}int main() {// 测试打印函数print(1, 2, 3); // 输出: 123print("Hello", " ", "World", "!"); // 输出: Hello World!print(1, " + ", 2, " = ", 3); // 输出: 1 + 2 = 3// 测试求和函数std::cout << "Sum: " << sum(1, 2, 3, 4, 5) << std::endl; // 输出: 15std::cout << "Sum: " << sum(1.5, 2.5, 3.5) << std::endl; // 输出: 7.5// 测试其他折叠操作std::cout << "Product: " << product(2, 3, 4) << std::endl; // 输出: 24std::cout << "All true: " << all_true(true, true, true) << std::endl; // 输出: 1std::cout << "Any true: " << any_true(false, true, false) << std::endl; // 输出: 1return 0;
}
模板高级特性
SFINAE(替换失败不是错误)
std::enable_if 工作原理
template<bool Condition, typename T = void>
struct enable_if;// 特化:当Condition为true时
template<typename T>
struct enable_if<true, T> {using type = T; // 定义type成员
};// 特化:当Condition为false时
template<typename T>
struct enable_if<false, T> {// 没有type成员!这会导致SFINAE
};
SFINAE(Substitution Failure Is Not An Error)机制:
-
当模板参数推导失败时,编译器不会报错,而是从候选函数中移除该模板
-
只有满足条件的模板才会被实例化
使用 constexpr if(C++17)
// 方法2:使用 constexpr if (C++17)
template <typename T>
auto process_modern(T value) {if constexpr (std::is_integral_v<T>) {std::cout << "Integral: " << value << std::endl;} else if constexpr (std::is_floating_point_v<T>) {std::cout << "Floating point: " << value << std::endl;} else {std::cout << "Other type" << std::endl;}return value;
}
constexpr if 特点
-
编译期求值:条件在编译时确定
-
分支消除:只有满足条件的分支会被编译
-
语法简洁:不需要多个模板重载
完整示例
#include <iostream>
#include <type_traits>
#include <string>template <typename T>
auto process_modern(T value) {if constexpr (std::is_integral_v<T>) {std::cout << "Integral: " << value;if constexpr (sizeof(T) == 1) {std::cout << " (char type)";} else if constexpr (sizeof(T) == 4) {std::cout << " (32-bit int)";}std::cout << std::endl;} else if constexpr (std::is_floating_point_v<T>) {std::cout << "Floating point: " << value;if constexpr (std::is_same_v<T, float>) {std::cout << " (float)";} else if constexpr (std::is_same_v<T, double>) {std::cout << " (double)";}std::cout << std::endl;} else if constexpr (std::is_pointer_v<T>) {std::cout << "Pointer to: " << *value << std::endl;} else if constexpr (std::is_same_v<T, std::string>) {std::cout << "String: \"" << value << "\" (length: " << value.length() << ")" << std::endl;} else {std::cout << "Other type: " << value << std::endl;}return value;
}int main() {process_modern(42); // Integralprocess_modern(3.14f); // Floating point (float)process_modern(2.718); // Floating point (double)int y = 200;process_modern(&y); // Pointerprocess_modern(std::string("Hello")); // Stringprocess_modern(true); // Other type (bool)return 0;
}
两种方法对比
SFINAE (std::enable_if) 方法
优点:
-
兼容 C++11
-
明确的函数重载,编译器可以更好地优化
-
可以用于类模板特化
缺点:
-
语法复杂,可读性差
-
需要写多个重载版本
-
错误信息不友好
constexpr if 方法
优点:
-
语法简洁,可读性好
-
只需要一个函数模板
-
更好的错误信息
-
可以嵌套使用
缺点:
-
需要 C++17 支持
-
所有分支必须在语法上有效(即使不被编译)
模板元编程
// 编译期计算阶乘
template <int N>
struct Factorial {static constexpr int value = N * Factorial<N - 1>::value;
};
/*
这是一个递归模板,通过 Factorial<N-1> 递归调用static constexpr 表示这是一个编译期常量计算公式:N! = N × (N-1)!
*///全特化(终止条件)
template <>
struct Factorial<0> {static constexpr int value = 1;
};
/*
为 N=0 提供特化,定义递归终止条件
数学定义:0! = 1
*/
// 使用
int main() {std::cout << Factorial<5>::value << std::endl; // 输出: 120return 0;
}
//当计算 Factorial<5>::value 时,编译器展开过程如下:
/*
Factorial<5>::value
= 5 * Factorial<4>::value
= 5 * (4 * Factorial<3>::value)
= 5 * (4 * (3 * Factorial<2>::value))
= 5 * (4 * (3 * (2 * Factorial<1>::value)))
= 5 * (4 * (3 * (2 * (1 * Factorial<0>::value))))
= 5 * (4 * (3 * (2 * (1 * 1)))) // Factorial<0>::value = 1
= 5 * (4 * (3 * (2 * 1)))
= 5 * (4 * (3 * 2))
= 5 * (4 * 6)
= 5 * 24
= 120
*/
完整代码
#include <iostream>// 编译期计算阶乘
template <int N>
struct Factorial {static constexpr int value = N * Factorial<N - 1>::value;
};template <>
struct Factorial<0> {static constexpr int value = 1;
};int main() {std::cout << "Factorial<0>::value = " << Factorial<0>::value << std::endl; // 1std::cout << "Factorial<1>::value = " << Factorial<1>::value << std::endl; // 1std::cout << "Factorial<5>::value = " << Factorial<5>::value << std::endl; // 120std::cout << "Factorial<10>::value = " << Factorial<10>::value << std::endl; // 3628800// 编译期验证static_assert(Factorial<0>::value == 1, "0! should be 1");static_assert(Factorial<5>::value == 120, "5! should be 120");static_assert(Factorial<10>::value == 3628800, "10! should be 3628800");// 可以在编译期常量需要的地方使用int array[Factorial<4>::value]; // 创建大小为24的数组std::cout << "数组大小: " << sizeof(array)/sizeof(array[0]) << std::endl;return 0;
}
更多示例
- 斐波那契数列
// 编译期计算斐波那契数列
template <int N>
struct Fibonacci {static constexpr int value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;
};template <>
struct Fibonacci<0> {static constexpr int value = 0;
};template <>
struct Fibonacci<1> {static constexpr int value = 1;
};int main() {std::cout << "Fibonacci<0> = " << Fibonacci<0>::value << std::endl; // 0std::cout << "Fibonacci<1> = " << Fibonacci<1>::value << std::endl; // 1std::cout << "Fibonacci<10> = " << Fibonacci<10>::value << std::endl; // 55return 0;
}
- 最大公约数(GCD)
// 编译期计算最大公约数
template <int A, int B>
struct GCD {static constexpr int value = GCD<B, A % B>::value;
};template <int A>
struct GCD<A, 0> {static constexpr int value = A;
};int main() {std::cout << "GCD(48, 18) = " << GCD<48, 18>::value << std::endl; // 6std::cout << "GCD(1071, 462) = " << GCD<1071, 462>::value << std::endl; // 21return 0;
}
概念(C++20)
// C++20 概念
template <typename T>
concept Integral = std::is_integral_v<T>;
/*
使用类型特征 std::is_integral_v 来定义概念满足条件的类型:int, long, short, char, bool 等不满足的类型:float, double, std::string 等
*/template <typename T>
concept Addable = requires(T a, T b) {{ a + b } -> std::same_as<T>;
};
/*
使用 requires 表达式定义概念检查类型 T 是否支持 + 运算符要求 a + b 的返回类型必须与 T 相同
*/// 使用概念约束模板
template <Integral T>
T add_integral(T a, T b) {return a + b;
}template <Addable T>
T add_any(T a, T b) {return a + b;
}// 使用
int main() {add_integral(5, 3); // OK// add_integral(3.14, 2.71); // 编译错误:不满足 Integral 概念add_any(5, 3); // OKadd_any(std::string("hello"), std::string("world")); // OKreturn 0;
}
完整代码
#include <iostream>
#include <type_traits>
#include <string>
#include <concepts> // C++20 概念头文件// 方法1:使用类型特征定义概念
template <typename T>
concept Integral = std::is_integral_v<T>;template <typename T>
concept FloatingPoint = std::is_floating_point_v<T>;// 方法2:使用 requires 表达式定义概念
template <typename T>
concept Addable = requires(T a, T b) {{ a + b } -> std::same_as<T>;
};template <typename T>
concept Subtractable = requires(T a, T b) {{ a - b } -> std::same_as<T>;
};template <typename T>
concept Printable = requires(T a) {{ std::cout << a } -> std::same_as<std::ostream&>;
};// 方法3:使用标准库预定义概念
template <typename T>
concept Arithmetic = std::integral<T> || std::floating_point<T>;// 使用概念约束模板函数
template <Integral T>
T add_integral(T a, T b) {std::cout << "Adding integers: " << a << " + " << b;T result = a + b;std::cout << " = " << result << std::endl;return result;
}template <Addable T>
T add_any(T a, T b) {std::cout << "Adding addable types: " << a << " + " << b;T result = a + b;std::cout << " = " << result << std::endl;return result;
}// 多个概念组合
template <typename T>
requires Addable<T> && Subtractable<T>
T calculate(T a, T b) {return (a + b) * (a - b);
}// 概念用于自动类型推导
Printable auto print_value(Printable auto value) {std::cout << "Value: " << value << std::endl;return value;
}int main() {// 测试 Integral 概念add_integral(5, 3); // OK// add_integral(3.14, 2.71); // 编译错误:不满足 Integral 概念// 测试 Addable 概念add_any(5, 3); // OKadd_any(std::string("hello"), std::string("world")); // OK// 测试多个概念组合std::cout << "Calculate: " << calculate(10, 3) << std::endl; // (10+3)*(10-3) = 91// 测试自动类型推导print_value(42); // OKprint_value("Hello"); // OK// print_value(std::vector<int>{}); // 编译错误:不满足 Printable 概念return 0;
}
模板实战示例
工厂模式模板
这是一个使用C++模板实现的通用工厂模式(Generic Factory Pattern),可以动态创建继承自同一基类的不同派生类对象。
template <typename Base>
class Factory {
private:using Creator = std::function<std::unique_ptr<Base>()>;
/*
定义了一个函数类型,该函数无参数,返回 std::unique_ptr<Base>每个注册的类都会对应一个这样的创建函数
*/std::unordered_map<std::string, Creator> creators;/*使用字符串作为键,创建函数作为值例如:"Circle" → 创建 Circle 对象的函数*/
public:template <typename Derived>void registerClass(const std::string& name) {creators[name] = []() -> std::unique_ptr<Base> {return std::make_unique<Derived>();};}/*使用 lambda 表达式捕获 Derived 类型信息当调用时,创建 Derived 对象并向上转型为 Base 指针*/std::unique_ptr<Base> create(const std::string& name) {auto it = creators.find(name);if (it != creators.end()) {return it->second();}return nullptr;}
};
完整代码
#include <iostream>
#include <memory>
#include <unordered_map>
#include <functional>
#include <string>template <typename Base>
class Factory {
private:using Creator = std::function<std::unique_ptr<Base>()>;std::unordered_map<std::string, Creator> creators;public:template <typename Derived>void registerClass(const std::string& name) {creators[name] = []() -> std::unique_ptr<Base> {return std::make_unique<Derived>();};}std::unique_ptr<Base> create(const std::string& name) {auto it = creators.find(name);if (it != creators.end()) {return it->second();}return nullptr;}// 添加更多实用方法bool isRegistered(const std::string& name) const {return creators.find(name) != creators.end();}std::vector<std::string> getRegisteredNames() const {std::vector<std::string> names;for (const auto& pair : creators) {names.push_back(pair.first);}return names;}
};// 基类和派生类定义
class Shape {
public:virtual void draw() = 0;virtual std::string getName() const = 0;virtual ~Shape() = default;
};class Circle : public Shape {
public:void draw() override { std::cout << "Drawing Circle ○" << std::endl; }std::string getName() const override { return "Circle"; }
};class Square : public Shape {
public:void draw() override { std::cout << "Drawing Square □" << std::endl; }std::string getName() const override { return "Square"; }
};class Triangle : public Shape {
public:void draw() override { std::cout << "Drawing Triangle △" << std::endl; }std::string getName() const override { return "Triangle"; }
};int main() {// 创建形状工厂Factory<Shape> shapeFactory;// 注册形状类shapeFactory.registerClass<Circle>("Circle");shapeFactory.registerClass<Square>("Square");shapeFactory.registerClass<Triangle>("Triangle");// 显示已注册的类型std::cout << "Registered shapes: ";for (const auto& name : shapeFactory.getRegisteredNames()) {std::cout << name << " ";}std::cout << std::endl;// 动态创建对象auto circle = shapeFactory.create("Circle");auto square = shapeFactory.create("Square");auto triangle = shapeFactory.create("Triangle");auto unknown = shapeFactory.create("Hexagon"); // 未注册的类型// 使用创建的对象if (circle) {circle->draw();std::cout << "Created: " << circle->getName() << std::endl;}if (square) {square->draw();std::cout << "Created: " << square->getName() << std::endl;}if (triangle) {triangle->draw();std::cout << "Created: " << triangle->getName() << std::endl;}if (!unknown) {std::cout << "Failed to create unknown shape: Hexagon" << std::endl;}return 0;
}
工厂模式的工作原理
- 注册阶段:将类名与创建函数关联
factory.registerClass<Circle>("Circle");
// 相当于:creators["Circle"] = 创建Circle对象的函数
- 创建阶段:根据名称查找并调用对应的创建函数
auto shape = factory.create("Circle");
// 查找 "Circle" → 调用对应的lambda → 返回 new Circle()
策略模式模板
// 排序策略模板
template <typename T, typename Compare = std::less<T>>
/*
1. 模板参数
typename T:要排序的元素类型typename Compare = std::less<T>:比较策略,默认是升序
*/class Sorter {
private:Compare comp;// 比较策略对象/*存储比较策略的实例可以是函数对象、函数指针或lambda表达式*/
public:void sort(std::vector<T>& vec) {std::sort(vec.begin(), vec.end(), comp);// 使用策略对象进行排序}
};
完整代码
#include <iostream>
#include <vector>
#include <algorithm>
#include <functional>// 排序策略模板
template <typename T, typename Compare = std::less<T>>
class Sorter {
private:Compare comp;public:void sort(std::vector<T>& vec) {std::sort(vec.begin(), vec.end(), comp);}// 添加更多实用方法void sort_range(std::vector<T>& vec, size_t start, size_t end) {if (start < end && end <= vec.size()) {std::sort(vec.begin() + start, vec.begin() + end, comp);}}bool is_sorted(const std::vector<T>& vec) const {return std::is_sorted(vec.begin(), vec.end(), comp);}
};int main() {std::vector<int> numbers = {3, 1, 4, 1, 5, 9, 2, 6};// 升序排序Sorter<int, std::less<int>> ascendingSorter;ascendingSorter.sort(numbers);std::cout << "升序排序: ";for (int n : numbers) std::cout << n << " ";std::cout << std::endl;// 降序排序Sorter<int, std::greater<int>> descendingSorter;descendingSorter.sort(numbers);std::cout << "降序排序: ";for (int n : numbers) std::cout << n << " ";std::cout << std::endl;return 0;
}
使用Lambda表达式作为策略
#include <iostream>
#include <vector>
#include <algorithm>int main() {std::vector<std::pair<int, std::string>> pairs = {{3, "three"}, {1, "one"}, {4, "four"}, {1, "one-again"}, {2, "two"}};// 使用lambda表达式作为比较策略auto pairCompare = [](const auto& a, const auto& b) {// 先按数字排序,数字相同按字符串排序if (a.first != b.first) {return a.first < b.first;}return a.second < b.second;};// C++17 CTAD (Class Template Argument Deduction)Sorter sorter{pairCompare};sorter.sort(pairs);std::cout << "排序后的键值对:" << std::endl;for (const auto& p : pairs) {std::cout << "{" << p.first << ", \"" << p.second << "\"}" << std::endl;}return 0;
}
策略模式的优势
-
灵活性:可以在运行时或编译时切换排序策略
-
可扩展性:轻松添加新的排序策略
-
复用性:同一套排序逻辑支持不同比较规则
-
类型安全:编译时检查比较策略的兼容性
-
性能:无虚函数开销,编译器可以内联优化
模板最佳实践
-
将模板实现放在头文件中:模板需要在编译时实例化
-
使用概念约束模板参数(C++20):提高代码可读性和错误信息
-
避免过度使用模板:模板会增加编译时间和代码体积
-
使用SFINAE或概念进行条件编译:提供更好的类型安全
-
考虑模板特化:为特定类型提供优化实现
模板是C++最强大的特性之一,合理使用可以编写出高效、类型安全且可重用的代码。
