第一章:模板的基础概念 (Template Fundamentals)
1.1 什么是模板?
模板 (Template) 是C++中的一种泛型编程 (Generic Programming) 机制,它允许我们编写与类型无关的代码。想象一下,如果我们要为不同的数据类型编写相同逻辑的函数,没有模板的话,我们需要为每种类型都写一遍代码。
模板的核心思想是代码复用 (Code Reuse) 和类型安全 (Type Safety)。编译器会根据实际使用的类型自动生成相应的代码,这个过程称为模板实例化 (Template Instantiation)。
1.2 模板的基本语法
// 基本模板语法结构template<typename T> // 或者使用 template<class T>返回类型 函数名(参数列表) { // 函数体}
注意:typename
和 class
在这里是等价的,但现代C++推荐使用 typename
,因为它更清晰地表达了T是一个类型参数。
第二章:函数模板 (Function Templates)
2.1 简单函数模板
让我们从一个简单的例子开始理解函数模板:
#include <iostream>
// 找出两个值中的最大值的模板函数template<typename T>T max_value(T a, T b) { return (a > b) ? a : b;}
int main() { // 编译器会自动推导类型 (Type Deduction) std::cout << max_value(10, 20) << std::endl; // T = int std::cout << max_value(3.14, 2.71) << std::endl; // T = double std::cout << max_value('a', 'z') << std::endl; // T = char
// 也可以显式指定类型 (Explicit Type Specification) std::cout << max_value<int>(10.5, 20.7) << std::endl; // 强制转换为int
return 0;}
2.2 多参数模板
模板可以有多个类型参数:
// 多类型参数的模板函数template<typename T1, typename T2>auto add_different_types(T1 a, T2 b) -> decltype(a + b) { return a + b;}
// C++14及以后可以简化为:template<typename T1, typename T2>auto add_different_types_v2(T1 a, T2 b) { return a + b; // 编译器自动推导返回类型}
2.3 非类型模板参数 (Non-type Template Parameters)
除了类型参数,模板还可以接受非类型参数:
#include <array>// 创建固定大小数组并初始化的模板函数template<typename T, std::size_t N>std::array<T, N> create_array(const T& initial_value) { std::array<T, N> arr; arr.fill(initial_value); return arr;}int main() { auto int_array = create_array<int, 5>(42); // 创建5个元素的int数组,都初始化为42 auto double_array = create_array<double, 3>(3.14); // 创建3个元素的double数组 return 0;}
第三章:类模板 (Class Templates)
3.1 基本类模板
类模板允许我们创建泛型类。让我们创建一个简单的栈 (Stack) 类:
#include <vector>#include <stdexcept>
template<typename T>class Stack {private: std::vector<T> elements; // 使用vector存储元素
public: // 入栈操作 void push(const T& element) { elements.push_back(element); }
// 出栈操作 T pop() { if (elements.empty()) { throw std::runtime_error("Stack is empty"); } T result = elements.back(); elements.pop_back(); return result; }
// 查看栈顶元素 const T& top() const { if (elements.empty()) { throw std::runtime_error("Stack is empty"); } return elements.back(); }
// 检查栈是否为空 bool empty() const { return elements.empty(); }
// 获取栈的大小 std::size_t size() const { return elements.size(); }};
// 使用示例int main() { Stack<int> int_stack; // 整数栈 Stack<std::string> str_stack; // 字符串栈
int_stack.push(10); int_stack.push(20);
str_stack.push("Hello"); str_stack.push("World");
std::cout << int_stack.pop() << std::endl; // 输出: 20 std::cout << str_stack.pop() << std::endl; // 输出: World
return 0;}
3.2 模板类的成员函数定义
有时我们需要将类模板的成员函数定义在类外部:
template<typename T>class Matrix {private: std::vector<std::vector<T>> data; std::size_t rows, cols;
public: Matrix(std::size_t r, std::size_t c); // 构造函数声明 T& at(std::size_t row, std::size_t col); // 元素访问函数声明 void print() const; // 打印函数声明};
// 构造函数定义(注意模板语法)template<typename T>Matrix<T>::Matrix(std::size_t r, std::size_t c) : rows(r), cols(c), data(r, std::vector<T>(c)) { // 初始化矩阵}
// 成员函数定义template<typename T>T& Matrix<T>::at(std::size_t row, std::size_t col) { if (row >= rows || col >= cols) { throw std::out_of_range("Matrix index out of range"); } return data[row][col];}
template<typename T>void Matrix<T>::print() const { for (const auto& row : data) { for (const auto& element : row) { std::cout << element << " "; } std::cout << std::endl; }}
第四章:模板特化 (Template Specialization)
4.1 全特化 (Full Specialization)
有时候,我们需要为特定的类型提供特殊的实现。这就是模板特化的用处:
#include <iostream>#include <string>
// 通用模板函数template<typename T>void process_data(const T& data) { std::cout << "处理通用数据: " << data << std::endl;}
// 为字符串类型提供特化版本template<>void process_data<std::string>(const std::string& data) { std::cout << "处理字符串数据 (特化版本): \"" << data << "\" (长度: " << data.length() << ")" << std::endl;}
// 为指针类型提供特化版本template<>void process_data<int*>(int* const& data) { if (data) { std::cout << "处理指针数据 (特化版本): 地址=" << data << ", 值=" << *data << std::endl; } else { std::cout << "处理空指针" << std::endl; }}
int main() { process_data(42); // 使用通用版本 process_data(3.14); // 使用通用版本 process_data(std::string("Hello World")); // 使用字符串特化版本
int value = 100; process_data(&value); // 使用指针特化版本
return 0;}
4.2 类模板的全特化
类模板也可以进行全特化:
#include <iostream>#include <cstring>// 通用的存储类模板template<typename T>class Storage {private: T data;public: Storage(const T& value) : data(value) {} void print() const { std::cout << "存储的数据: " << data << std::endl; } T get() const { return data; }};// 为const char*提供特化版本(处理C风格字符串)template<>class Storage<const char*> {private: char* data;public: Storage(const char* str) { data = new char[std::strlen(str) + 1]; std::strcpy(data, str); } ~Storage() { delete[] data; } // 复制构造函数 Storage(const Storage& other) { data = new char[std::strlen(other.data) + 1]; std::strcpy(data, other.data); } // 赋值操作符 Storage& operator=(const Storage& other) { if (this != &other) { delete[] data; data = new char[std::strlen(other.data) + 1]; std::strcpy(data, other.data); } return *this; } void print() const { std::cout << "存储的C字符串 (特化版本): \"" << data << "\"" << std::endl; } const char* get() const { return data; }};int main() { Storage<int> int_storage(42); Storage<const char*> str_storage("Hello World"); int_storage.print(); // 使用通用版本 str_storage.print(); // 使用特化版本 return 0;}
4.3 偏特化 (Partial Specialization)
偏特化只能用于类模板,不能用于函数模板。它允许我们为模板参数的某些组合提供特殊实现:
#include <iostream> // 包含标准输入输出库,用于后续的输入和输出操作
// 通用的配对类模板// 定义一个模板类 Pair,它能够存储一个配对数据,支持两种不同类型 T1 和 T2template<typename T1, typename T2>class Pair {private: T1 first; // 定义一个变量 first,类型为 T1,用来存储配对中的第一个元素 T2 second; // 定义一个变量 second,类型为 T2,用来存储配对中的第二个元素public: // 构造函数,使用常量引用传参,并利用初始化列表初始化成员变量 Pair(const T1& f, const T2& s) : first(f), second(s) {}
// 成员函数 print,用于打印配对的内容(不修改成员变量,故为 const 成员函数) void print() const { // 输出格式为:通用配对: (first, second) std::cout << "通用配对: (" << first << ", " << second << ")" << std::endl; }};
// 偏特化:当两个类型相同时// 下面定义 Pair 的偏特化版本,当两个模板参数类型相同时(即 T1 和 T2 都是 T)使用此版本template<typename T>class Pair<T, T> {private: T first; // 定义变量 first,类型为 T T second; // 定义变量 second,类型为 Tpublic: // 构造函数,同样使用初始化列表初始化两个成员变量 Pair(const T& f, const T& s) : first(f), second(s) {}
// 重写 print 函数,以显示偏特化的信息 void print() const { // 输出格式为:相同类型配对 (偏特化): (first, second) std::cout << "相同类型配对 (偏特化): (" << first << ", " << second << ")" << std::endl; }
// 额外功能:检查两个值是否相等 // 定义一个成员函数 are_equal(),用于比较 first 和 second 是否相等,并返回布尔值 bool are_equal() const { return first == second; }};
// 偏特化:当第二个类型是指针时// 定义 Pair 的另一个偏特化版本,当第二个模板参数为指针类型 T2* 时使用此版本template<typename T1, typename T2>class Pair<T1, T2*> {private: T1 first; // 定义变量 first,类型为 T1 T2* second; // 定义变量 second,类型为 T2*,即指针类型public: // 构造函数,接受 T1 类型的值和 T2 指针,通过初始化列表初始化成员变量 Pair(const T1& f, T2* s) : first(f), second(s) {}
// 重写 print 函数,专门处理指针类型的输出 void print() const { // 输出起始部分:显示 "指针配对 (偏特化): (first, " std::cout << "指针配对 (偏特化): (" << first << ", "; // 判断指针 second 是否为空 if (second) { // 如果 second 非空,输出指针所指向的数据(解引用) std::cout << *second; } else { // 如果 second 为空,输出 "nullptr" std::cout << "nullptr"; } // 输出闭合括号并换行 std::cout << ")" << std::endl; }};
int main() { // 创建一个 Pair 对象 p1,模板参数为 <int, double>,调用通用版本构造函数 Pair<int, double> p1(1, 2.5); // 使用通用版本:当类型不同 // 创建一个 Pair 对象 p2,模板参数为 <int, int>,因此调用相同类型的偏特化版本 Pair<int, int> p2(3, 4); // 使用偏特化版本(相同类型)
int value = 100; // 定义一个 int 类型的变量 value,用于测试指针配对
// 创建一个 Pair 对象 p3,模板参数为 <std::string, int*>,调用第二个偏特化版(指针类型) Pair<std::string, int*> p3("数值", &value); // 使用偏特化版本(指针类型)
// 调用 p1 的 print 函数打印通用配对数据 p1.print();
// 调用 p2 的 print 函数打印相同类型偏特化配对数据 p2.print();
// 调用 p2 的 are_equal 函数检查 p2 中两个值是否相等,并将结果打印出来 std::cout << "p2中的值相等吗? " << (p2.are_equal() ? "是" : "否") << std::endl;
// 调用 p3 的 print 函数打印指针配对数据,并正确显示指针指向的值 p3.print();
return 0; // 返回 0 表示程序正常结束}
第五章:高级模板技术
5.1 SFINAE (Substitution Failure Is Not An Error)
SFINAE是C++模板元编程中的一个重要概念。当模板参数替换失败时,编译器不会报错,而是简单地从重载候选中移除该模板:
#include <iostream>#include <type_traits>// 使用SFINAE检测类型是否有特定成员函数template<typename T>class has_size_method {private: // 这个技巧用于检测T是否有size()方法 template<typename U> static auto test(int) -> decltype(std::declval<U>().size(), std::true_type{}); template<typename> static std::false_type test(...);public: static constexpr bool value = decltype(test<T>(0))::value;};// 根据是否有size方法选择不同的实现template<typename T>typename std::enable_if<has_size_method<T>::value, std::size_t>::typeget_container_info(const T& container) { std::cout << "容器有size方法,大小为: "; return container.size();}template<typename T>typename std::enable_if<!has_size_method<T>::value, std::size_t>::typeget_container_info(const T& container) { std::cout << "容器没有size方法,无法获取大小。返回: "; return 0;}int main() { std::vector<int> vec{1, 2, 3, 4, 5}; int arr[] = {1, 2, 3}; std::cout << get_container_info(vec) << std::endl; // vector有size方法 std::cout << get_container_info(arr) << std::endl; // 数组没有size方法 return 0;}
5.2 变参模板 (Variadic Templates)
C++11引入了变参模板,允许模板接受可变数量的参数:
#include <iostream> // 包含标准输入输出流库,用于后续的输入输出操作
// 递归版本的变参模板函数// 定义处理只有一个参数的模板函数,当只剩下最后一个参数时调用该函数template<typename T>void print_values(const T& value) { // 递归的基础情况:输出传入的单个参数并换行 std::cout << value << std::endl;}
// 定义处理多个参数的模板函数,利用变参模板实现递归调用template<typename T, typename... Args>void print_values(const T& first, const Args&... rest) { // 输出第一个参数并在后面接一个空格 std::cout << first << " "; // 递归调用print_values,将剩余参数传入 print_values(rest...); // 递归调用,逐个处理剩余参数}
// C++17折叠表达式版本 (Fold Expression)// 定义使用折叠表达式实现的变参函数,一次性展开所有参数进行打印template<typename... Args>void print_values_cpp17(const Args&... args) { // 利用折叠表达式对每个参数执行 std::cout << 参数 << " " 的操作 ((std::cout << args << " "), ...); // 所有参数打印完成后输出换行符 std::cout << std::endl;}
// 计算多个值的和// 定义一个使用折叠表达式计算传入所有参数之和的模板函数template<typename... Args>auto sum(Args... args) { // 利用折叠表达式 (args + ...) 计算所有参数累加的结果 return (args + ...); // C++17折叠表达式,依次累加所有参数}
// 变参模板类:元组的简单实现// 声明模板类SimpleTuple,可以接收任意数量和类型的参数,用于保存一组数据template<typename... Types>class SimpleTuple;
// 特化:空元组// 当没有传入任何类型参数时,提供一个空特化版本,作为递归终止条件template<>class SimpleTuple<> {};
// 递归定义:头部+尾部// 当有至少一个参数时,将第一个元素作为头部,其余元素构成尾部元组template<typename Head, typename... Tail>class SimpleTuple<Head, Tail...> {private: Head head; // 保存元组的第一个元素,即头部 SimpleTuple<Tail...> tail; // 保存剩余的元素(尾部),以一个SimpleTuple表示public: // 构造函数,使用初始化列表依次初始化头部元素和尾部元组 SimpleTuple(Head h, Tail... t) : head(h), tail(t...) {}
// 返回头部元素的引用,可供外部访问和修改 Head& get_head() { return head; } // 常量版本,返回头部元素的常量引用,保护数据不被修改 const Head& get_head() const { return head; }
// 返回尾部(子元组)的引用,用于递归访问余下元素 SimpleTuple<Tail...>& get_tail() { return tail; } // 常量版本,返回尾部(子元组)的常量引用 const SimpleTuple<Tail...>& get_tail() const { return tail; }};
int main() { // 测试变参函数:使用递归版本打印多个参数 print_values(1, 2.5, "Hello", 'A');
// 测试变参函数:使用 C++17 折叠表达式版本打印多个参数 print_values_cpp17(1, 2.5, "World", 'B');
// 测试求和函数:依次累加整数和浮点数 std::cout << "1+2+3+4+5 = " << sum(1, 2, 3, 4, 5) << std::endl; std::cout << "1.5+2.7+3.2 = " << sum(1.5, 2.7, 3.2) << std::endl;
// 测试简单元组的实现 // 创建一个SimpleTuple对象,包含 int、double 和 std::string 三种类型的元素 SimpleTuple<int, double, std::string> tuple(42, 3.14, "Hello"); // 访问并打印元组中的头部元素 std::cout << "元组第一个元素: " << tuple.get_head() << std::endl; return 0; // 返回0表示程序正常结束}
第六章:模板元编程 (Template Metaprogramming)
6.1 编译期计算
模板可以在编译期进行计算,这是模板元编程的基础:
#include <iostream> // 包含标准输入输出流库,用于后续在终端输出结果
// 编译期计算阶乘// 定义模板结构体 Factorial,通过模板递归在编译期计算阶乘的值template<int N>struct Factorial { // 定义静态 constexpr 成员 value,其值为 N 乘以 Factorial<N - 1> 的值 static constexpr int value = N * Factorial<N - 1>::value;};
// 特化:递归终止条件// 当 N == 0 时,阶乘定义为1,此特化用于递归终止,防止无限递归template<>struct Factorial<0> { // 定义静态 constexpr 成员 value,其值为 1 static constexpr int value = 1;};
// 编译期计算斐波那契数列// 定义模板结构体 Fibonacci,通过模板递归在编译期计算斐波那契数template<int N>struct Fibonacci { // 定义静态 constexpr 成员 value,其值为 Fibonacci<N - 1> 与 Fibonacci<N - 2> 的和 static constexpr int value = Fibonacci<N - 1>::value + Fibonacci<N - 2>::value;};
// 特化:递归终止条件(第一种情况)// 当 N == 0 时,斐波那契数值定义为 0,此特化用于递归终止template<>struct Fibonacci<0> { // 定义静态 constexpr 成员 value,其值为 0 static constexpr int value = 0;};
// 特化:递归终止条件(第二种情况)// 当 N == 1 时,斐波那契数值定义为 1,此特化用于递归终止template<>struct Fibonacci<1> { // 定义静态 constexpr 成员 value,其值为 1 static constexpr int value = 1;};
// 使用constexpr函数 (C++11及以后的现代方法)// 定义 constexpr 函数 factorial_func,通过递归在编译期或运行期计算阶乘constexpr int factorial_func(int n) { // 如果 n 小于等于1,则返回1;否则返回 n 乘以 factorial_func(n - 1) return (n <= 1) ? 1 : n * factorial_func(n - 1);}
// 定义 constexpr 函数 fibonacci_func,通过递归在编译期或运行期计算斐波那契数列的值constexpr int fibonacci_func(int n) { // 如果 n 小于等于1,则直接返回 n;否则返回 fibonacci_func(n - 1) 与 fibonacci_func(n - 2) 的和 return (n <= 1) ? n : fibonacci_func(n - 1) + fibonacci_func(n - 2);}
int main() { // 这些值在编译期就计算好了 // 通过模板方式计算5的阶乘,并将结果赋予 fact5,其值应为120 constexpr int fact5 = Factorial<5>::value; // 120 // 通过模板方式计算第10个斐波那契数,并将结果赋予 fib10,其值应为55 constexpr int fib10 = Fibonacci<10>::value; // 55 // 使用 constexpr 函数计算5的阶乘,赋值给 fact5_func,结果同样为120 constexpr int fact5_func = factorial_func(5); // 120 // 使用 constexpr 函数计算第10个斐波那契数,赋值给 fib10_func,结果同样为55 constexpr int fib10_func = fibonacci_func(10); // 55
// 输出模板版本和 constexpr函数版本计算的结果 std::cout << "5! = " << fact5 << " (模板版本)" << std::endl; std::cout << "5! = " << fact5_func << " (constexpr函数版本)" << std::endl; std::cout << "fibonacci(10) = " << fib10 << " (模板版本)" << std::endl; std::cout << "fibonacci(10) = " << fib10_func << " (constexpr函数版本)" << std::endl;
return 0; // 返回0表示程序正常结束}
6.2 类型萃取 (Type Traits)
类型萃取用于在编译期获取类型的信息:
#include <iostream> // 包含输入输出流库,用于输出信息#include <type_traits> // 包含标准类型萃取工具,比如 std::is_integral 等
// 自定义的类型萃取:检查是否是指针类型// 默认情况下,所有类型都不是指针,所以 value 为 falsetemplate<typename T>struct is_pointer { static constexpr bool value = false; // 非指针类型时,值为 false};
// 当类型为指针时(T*),特化版本将 value 设置为 truetemplate<typename T>struct is_pointer<T*> { static constexpr bool value = true; // 指针类型时,值为 true};
// 移除指针的类型萃取:如果类型是指针,则返回指针指向的类型;否则返回原类型// 默认情况,不是指针的类型,移除指针后仍为 Ttemplate<typename T>struct remove_pointer { using type = T;};
// 特化版本:对于指针类型 T*,移除指针后得到的类型为 Ttemplate<typename T>struct remove_pointer<T*> { using type = T;};
// 辅助模板别名 (C++14):简化 remove_pointer<T>::type 的写法template<typename T>using remove_pointer_t = typename remove_pointer<T>::type;
// 条件类型选择:根据布尔条件选择类型// 当 Condition 为 true 时,type 定义为 TrueTypetemplate<bool Condition, typename TrueType, typename FalseType>struct conditional { using type = TrueType;};
// 当 Condition 为 false 时,特化版本定义 type 为 FalseTypetemplate<typename TrueType, typename FalseType>struct conditional<false, TrueType, FalseType> { using type = FalseType;};
// 辅助模板别名 (C++14):简化 conditional 的写法template<bool Condition, typename TrueType, typename FalseType>using conditional_t = typename conditional<Condition, TrueType, FalseType>::type;
// 使用类型萃取的函数模板// 此函数分析传入类型 T 的特性,并输出相关信息template<typename T>void analyze_type(const T& value) { std::cout << "分析类型信息:" << std::endl; // 判断类型 T 是否为指针类型,输出结果 std::cout << " 是否是指针: " << (is_pointer<T>::value ? "是" : "否") << std::endl; // 利用标准类型萃取判断是否为整数类型 std::cout << " 是否是整数: " << (std::is_integral<T>::value ? "是" : "否") << std::endl; // 利用标准类型萃取判断是否为浮点数类型 std::cout << " 是否是浮点数: " << (std::is_floating_point<T>::value ? "是" : "否") << std::endl; // 输出类型 T 的大小(字节数) std::cout << " 类型大小: " << sizeof(T) << " 字节" << std::endl; // 如果 T 是指针类型,则输出指针指向的类型大小 if constexpr (is_pointer<T>::value) { std::cout << " 指向的类型大小: " << sizeof(remove_pointer_t<T>) << " 字节" << std::endl; } // 换行分割 std::cout << std::endl;}
int main() { int x = 42; // 定义一个 int 类型变量 x,并赋值 42 int* px = &x; // 定义一个 int* 类型指针 px,指向变量 x double y = 3.14; // 定义一个 double 类型变量 y,并赋值 3.14
analyze_type(x); // 分析 int 类型的信息 analyze_type(px); // 分析 int* 指针类型的信息 analyze_type(y); // 分析 double 类型的信息
// 使用条件类型选择:当 sizeof(int) >= 4 为真时,选择 int 类型;否则选择 double 类型 using int_or_double = conditional_t<sizeof(int) >= 4, int, double>; std::cout << "条件选择的类型大小: " << sizeof(int_or_double) << " 字节" << std::endl;
return 0; // 程序正常结束,返回 0}
第七章:现代C++模板特性
7.1 概念 (Concepts) - C++20
概念允许我们对模板参数施加约束,使模板更易于使用和理解:
// 注意:这需要C++20支持// 这行注释说明本代码需要使用C++20标准,因为接下来将使用 concepts 相关的语言特性
#include <iostream> // 引入输入输出流库,用于输出信息#include <concepts> // 引入 C++20 的 concepts 库,用于定义概念
// 定义一个概念:可打印的类型template<typename T>concept Printable = requires(T a) { std::cout << a; // 要求类型 T 能够被输出到 std::cout,必须支持 << 运算符};
// 定义一个概念:有 size 方法的类型template<typename T>concept HasSize = requires(T a) { a.size(); // 要求类型 T 必须有一个 size() 方法 { a.size() } -> std::convertible_to<std::size_t>; // 并且 size() 方法的返回值必须可以转换为 std::size_t 类型};
// 使用概念约束的函数模板:只对 Printable 类型有效template<Printable T>void safe_print(const T& value) { // 当传入的类型符合 Printable 概念时,可以安全地打印该值 std::cout << "安全打印: " << value << std::endl;}
// 使用概念约束的函数模板:只对具有 size() 方法的容器类型有效template<HasSize Container>void print_container_size(const Container& container) { // 输出容器的大小 std::cout << "容器大小: " << container.size() << std::endl;}
// 更复杂的概念组合// 定义一个 Number 概念,要求类型 T 必须是整数类型或浮点类型template<typename T>concept Number = std::integral<T> || std::floating_point<T>;// 使用标准库中的 std::integral 和 std::floating_point 概念进行判断
// 定义一个平方函数 square,接受符合 Number 概念的类型参数template<Number T>T square(T value) { // 返回传入值的平方 return value * value;}
7.2 自动类型推导的改进
现代C++在类型推导方面有很多改进:
#include <iostream> // 引入输入输出流库,用于输出信息#include <vector> // 引入 vector 容器,用于存储动态数组数据#include <map> // 引入 map 容器,尽管在本示例中未被使用,通常用于存储键值对
// C++17 类模板参数推导 (CTAD) 示例int main() { // 不需要显式指定模板参数,编译器会根据初始化列表中的元素类型推导模板参数类型 std::vector vec{1, 2, 3, 4, 5}; // 上述语句中,编译器会推导出 vec 为 std::vector<int>,因为所有元素都是 int 类型
// 使用 std::pair,也可利用 CTAD 自动推导类型 std::pair p{42, 3.14}; // 这里 p 被推导为 std::pair<int, double>,因为第一个元素是 int,第二个元素是 double
// 定义一个泛型 lambda 表达式,利用 auto 参数来实现模板化的函数 auto lambda = [](auto x, auto y) { return x + y; }; // lambda 是一个泛型函数对象,可以接受任意类型参数,只要它们支持加法运算
// 调用 lambda,下面的调用将自动推导出参数类型,并根据实际参数执行相应的加法操作 std::cout << lambda(1, 2) << std::endl; // 传入两个 int 类型的参数,结果为 int 类型相加 std::cout << lambda(1.5, 2.7) << std::endl; // 传入两个 double 类型的参数,结果为 double 类型相加 std::cout << lambda(1, 2.5) << std::endl; // 传入 int 与 double 类型的参数,编译器会进行类型提升,结果为 double
return 0; // 程序正常结束}
总结与最佳实践
通过这个教程,我们深入了解了C++模板系统的各个方面。模板是C++中最强大的特性之一,它支持泛型编程、元编程和类型安全的代码复用。
关键要点回顾:
模板基础: 模板允许编写与类型无关的代码,编译器根据使用情况自动生成具体的代码实例。
函数模板: 用于创建泛型函数,支持类型推导和显式类型指定。
类模板: 用于创建泛型类,可以有多个模板参数,包括类型参数和非类型参数。
模板特化:
全特化:为特定类型提供完全不同的实现
偏特化:为模板参数的某些组合提供特殊实现(仅限类模板)
高级技术:
SFINAE:利用替换失败进行条件编译
变参模板:处理可变数量的参数
模板元编程:在编译期进行计算和类型操作
现代特性:
概念 (C++20):为模板参数添加约束
改进的类型推导:CTAD、auto、泛型lambda
使用建议:
优先使用标准库提供的模板和算法
编写模板时要考虑错误信息的可读性
合理使用特化,不要过度复杂化
在现代C++中,优先使用constexpr而不是模板元编程进行编译期计算
使用概念 (如果有C++20支持) 来使模板更易于理解和使用
模板是C++的核心特性,掌握它们将大大提升您的C++编程能力。记住,模板的学习是一个渐进的过程,从简单的泛型函数开始,逐步深入到更高级的技术。