当前位置: 首页 > news >正文

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;
}

折叠表达式的四种形式

  1. 一元右折叠 (args op …)
template <typename... Args>
auto right_fold(Args... args) {return (args + ...);  // 展开为: arg1 + (arg2 + (arg3 + ...))
}
  1. 一元左折叠 (… op args)
template <typename... Args>
auto left_fold(Args... args) {return (... + args);  // 展开为: ((arg1 + arg2) + arg3) + ...
}
  1. 二元右折叠 (args op … op init)
template <typename... Args>
auto binary_right_fold(Args... args) {return (args + ... + 0);  // 展开为: arg1 + (arg2 + (arg3 + 0))
}
  1. 二元左折叠 (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;
}

更多示例

  1. 斐波那契数列
// 编译期计算斐波那契数列
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;
}
  1. 最大公约数(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++最强大的特性之一,合理使用可以编写出高效、类型安全且可重用的代码。

http://www.dtcms.com/a/590015.html

相关文章:

  • GradNorm
  • 企业做网站公司有哪些网站开发所需费用
  • 【TaskStackListener】Android 中用于监听和响应任务栈
  • 网站方案建设书怎么写国外最开放的浏览器是哪个
  • 【图像理解进阶】视频总结最新研究成果:从SOTA模型到实操落地(2025最新版)
  • 国内包装设计网站条形码生成器在线制作图片
  • 建设玩外汇平台网站wordpress 分类小工具
  • 数据结构---时空复杂度
  • 万维网站续费多少一年在免费空间上传网站为什么访问不了
  • win系统更新ios平台更新说明
  • WSL从C盘迁移到其他盘区,释放存储空间
  • Docker零基础入门
  • 上海网站搜索优化太原论坛2021
  • 【QT开发】Ubuntu搭建QT开发环境
  • 东莞做营销网站建设网站建设 php 企业网站
  • 递归动漫讲解咯
  • 男和男做的视频网站宿迁房产网签备案查询系统
  • 好用的Windows工具
  • 公司发布网站需要备案吗专业的建网站公司地址
  • C++ 从入门到进阶:核心知识与学习指南
  • 怎么获得免费网站首饰设计网站推荐
  • 做网站是买服务器还是买cdn微信页面
  • 网上书城网站开发自学网站开发软件开发
  • 门户网站广告的类型wordpress 修改字体
  • 混合式教学财务管理网站建设网站 设计案例
  • 搭建LNMP私有云存储
  • Zabbix监控K8S的PV卷
  • 电商网站开发视频中国最厉害的营销策划公司
  • MyBatis-Plus 通用 CRUD 实现原理技术文档
  • 通俗易懂:YOLO模型原理详解,从零开始理解目标检测