C++基础语法2-模板

张开发
2026/4/11 10:25:31 15 分钟阅读

分享文章

C++基础语法2-模板
C模板一、模板的基本概念1.1 为什么需要模板在没有模板时我们需要为不同类型编写重复代码// 问题多个函数实现相同逻辑 int max_int(int a, int b) { return a b ? a : b; } double max_double(double a, double b) { return a b ? a : b; } string max_string(const string a, const string b) { return a b ? a : b; } // 每次添加新类型都要新写一个函数模板解决了这个问题// 解决方案一个模板搞定所有类型 templatetypename T T max(T a, T b) { return a b ? a : b; } // 编译器自动生成具体版本 max(1, 2); // 生成 int 版本 max(3.14, 2.71); // 生成 double 版本 max(hi, bye); // 生成 const char* 版本函数模板详解函数模板为函数设计模板2.1基本语法templatetypename T // 或 templateclass T两者等价 T add(T a, T b) { return a b; }templatetypename T模板参数列表T类型参数表示任意类型实例化编译器看到调用时生成具体函数templatetypename T声明模板T是一个类型参数type parameterT add(...)函数的返回类型是 TT aT b--第一个参数 a的类型是 T第二个参数 b的类型是 Treturn a b;返回 T类型的结果当你调用add(5, 3)时编译器看到参数5和3都是int推导出T应该是int生成一个具体的函数int add(int a, int b) { // 注意这是实际生成的代码 return a b; }2.2 多类型模板参数// 多个类型参数 templatetypename T1, typename T2, typename T3 auto add_three(T1 a, T2 b, T3 c) - decltype(a b c) { return a b c; }templatetypename T1, typename T2, typename T3声明三个类型参数auto占位符表示返回类型稍后指定add_three(T1 a, T2 b, T3 c)函数名和参数列表- decltype(a b c)获取表达式a b c在编译时的结果类型但不会实际计算这个表达式。尾置返回类型声明。C11满足。{ return a b c; }真正的函数体// 类型推导 auto result1 add_three(1, 2.5, 3.0f); // T1int, T2double, T3float auto result2 add_three(1.5, 2, 3); // 返回类型自动推导 // 默认模板参数C14起 templatetypename T int, typename U double auto multiply(T a 1, U b 1.0) { return a * b; } auto result1 multiply(); // 使用默认int和double // 推导过程 // T 使用默认 int // U 使用默认 double // a 使用默认 1 (int) // b 使用默认 1.0 (double) // 返回类型: int * double → double auto result2 multiplyfloat(); // Tfloat, Udouble // 推导过程 // T 显式指定为 float // U 使用默认 double // a 使用默认 1 (转换为 float 1.0f) // b 使用默认 1.0 (double) // 返回类型: float * double → double auto result3 multiply(3.5f); // Tfloat, Udouble // 推导过程 // 表示使用模板但不指定类型参数 // 3.5f 是 float所以 a 3.5f // 从 a 推导 T float // U 使用默认 double // b 使用默认 1.0 (double) // 返回类型: float * double → double auto r4 multiplyfloat, int(3.5f, 2) // Tfloat, Uint, 返回类型: float * int → float编译器看到return a * b根据a * b的结果类型推导函数的返回类型这需要C14或更高版本C11的版本就要用decltemplatetypename T int, typename U double auto multiply(T a 1, U b 1.0) - decltype(a * b) { // 尾置返回类型 return a * b; }类模板详解类模板为类设计模板3.1 基本类模板// 栈类模板 templatetypename T, size_t Capacity 100 class Stack { private: T data[Capacity]; size_t top_index 0; public: // 成员函数在类内定义 void push(const T value) { if (top_index Capacity) { data[top_index] value; } } T pop() { if (top_index 0) { return data[--top_index]; } throw std::out_of_range(Stack is empty); } // 类外定义成员函数 bool empty() const; size_t size() const; T top(); }; // 类外定义成员函数 templatetypename T, size_t Capacity bool StackT, Capacity::empty() const { return top_index 0; } templatetypename T, size_t Capacity size_t StackT, Capacity::size() const { return top_index; } templatetypename T, size_t Capacity T StackT, Capacity::top() { if (top_index 0) { return data[top_index - 1]; } throw std::out_of_range(Stack is empty); }模板声明与设计思想1.1 模板参数templatetypename T, size_t Capacity 100typename T类型参数表示栈中存储的元素类型size_t Capacity 100非类型模板参数表示栈的最大容量默认100这是一个混合模板既有类型参数也有值参数1.2 固定大小数组的设计private: T data[Capacity]; // 固定大小数组为什么用固定数组而不是动态数组如vector零动态内存分配栈空间在编译时确定更好的缓存局部性连续内存访问实时系统友好无内存分配延迟内存使用确定不会超过Capacity容量n.二、数据成员详解private: T data[Capacity]; // 存储元素的数组 size_t top_index 0; // 栈顶指针下一个可用位置2.1 内存布局示例// 当声明 Stackint, 5 时 // 内存布局 // data[0]: 未使用 // data[1]: 未使用 // data[2]: 未使用 // data[3]: 未使用 // data[4]: 未使用 // top_index 0 // push(10) 后 // data[0]: 10 // top_index 1 // push(20) 后 // data[0]: 10 // data[1]: 20 // top_index 2三、成员函数详细分析3.1 push 方法cppcpp下载复制void push(const T value) { if (top_index Capacity) { // 1. 检查是否已满 data[top_index] value; // 2. 存储并移动指针 } // 3. 如果已满静默失败可以改进为抛出异常 }执行过程cppcpp下载复制Stackint, 3 s; s.push(10); // top_index0 → 存储到data[0] → top_index1 s.push(20); // top_index1 → 存储到data[1] → top_index2 s.push(30); // top_index2 → 存储到data[2] → top_index3 s.push(40); // top_index3 Capacity3 → 条件不成立不存储3.2 pop 方法cppcpp下载复制T pop() { if (top_index 0) { // 1. 检查是否为空 return data[--top_index]; // 2. 先减指针再返回 } throw std::out_of_range(Stack is empty); }重要细节先--top_index再返回因为top_index指向下一个可用位置返回的是值拷贝不是引用如果栈为空抛出异常执行过程cppcpp下载复制Stackint, 3 s; s.push(10); s.push(20); s.push(30); // top_index3 int a s.pop(); // top_index3 → --top_index2 → 返回data[2]30 int b s.pop(); // top_index2 → --top_index1 → 返回data[1]203.3 类外定义的成员函数3.3.1 empty 方法cppcpp下载复制templatetypename T, size_t Capacity bool StackT, Capacity::empty() const { return top_index 0; }StackT, Capacity::指定这是哪个类的成员函数const承诺不修改对象状态简单判断top_index是否为03.3.2 size 方法cppcpp下载复制templatetypename T, size_t Capacity size_t StackT, Capacity::size() const { return top_index; // 当前元素个数 }注意size()返回的是实际元素数量不是容量与标准库容器的size()行为一致3.3.3 top 方法cppcpp下载复制templatetypename T, size_t Capacity T StackT, Capacity::top() { if (top_index 0) { return data[top_index - 1]; // 返回引用可修改 } throw std::out_of_range(Stack is empty); }关键点返回引用可以修改栈顶元素不弹出元素只查看需要检查top_index - 1是否有效3.2 类模板的使用// 实例化类模板 Stackint int_stack; // Tint, Capacity100 Stackdouble, 50 double_stack; // Tdouble, Capacity50 Stackstd::string str_stack; // Tstd::string // 使用别名简化C11 templatetypename T using SmallStack StackT, 10; SmallStackint small_int_stack; // C17 CTAD类模板参数推导 std::pair p(1, 3.14); // 推导为 std::pairint, double std::vector v{1, 2, 3}; // 推导为 std::vectorint // 对于自定义类需要提供推导指引 templatetypename T class MyVector { std::vectorT data; public: MyVector(std::initializer_listT init) : data(init) {} }; // 推导指引 templatetypename T MyVector(std::initializer_listT) - MyVectorT; MyVector mv{1, 2, 3}; // 推导为 MyVectorint四、模板特化与偏特化4.1 全特化// 通用模板 templatetypename T class TypeInfo { public: static const char* name() { return unknown; } }; // 全特化为特定类型提供特殊实现 template class TypeInfoint { public: static const char* name() { return int; } }; template class TypeInfodouble { public: static const char* name() { return double; } }; template class TypeInfostd::string { public: static const char* name() { return std::string; } }; // 使用 std::cout TypeInfoint::name(); // 输出: int std::cout TypeInfodouble::name(); // 输出: double std::cout TypeInfofloat::name(); // 输出: unknown4.2 偏特化部分特化// 原始模板 templatetypename T, typename U class MyPair { T first; U second; public: void print() { std::cout generic pair\n; } }; // 偏特化1第二个类型是指针 templatetypename T, typename U class MyPairT, U* { T first; U* second; public: void print() { std::cout pair with pointer\n; } }; // 偏特化2两个类型相同 templatetypename T class MyPairT, T { T first; T second; public: void print() { std::cout pair with same types\n; } }; // 偏特化3非类型参数 templatetypename T, size_t N class FixedArray { T data[N]; public: size_t size() const { return N; } }; templatetypename T class FixedArrayT, 0 { // 特化大小为0的数组 public: size_t size() const { return 0; } // 不包含data成员 };五、可变参数模板Variadic Templates5.1 基本用法// 递归终止条件 void print() { std::cout std::endl; } // 可变参数模板 templatetypename T, typename... Args void print(T first, Args... args) { std::cout first ; print(args...); // 递归调用 } // 使用 print(1, 2.5, hello, a); // 输出: 1 2.5 hello a5.2 完美转发// 通用工厂函数 templatetypename T, typename... Args T* create(Args... args) { return new T(std::forwardArgs(args)...); } // 使用 class Widget { public: Widget(int a, double b, const std::string c) { std::cout Widget: a , b , c \n; } }; auto w createWidget(42, 3.14, test);5.3 折叠表达式C17// C11/14的递归求和 templatetypename T T sum(T value) { return value; } templatetypename T, typename... Args T sum(T first, Args... args) { return first sum(args...); } // C17折叠表达式 templatetypename... Args auto sum_fold(Args... args) { return (... args); // 左折叠((arg1 arg2) arg3)... } templatetypename... Args auto sum_fold_right(Args... args) { return (args ...); // 右折叠arg1 (arg2 arg3)... } // 更多折叠表达式示例 templatetypename... Args void print_all(Args... args) { (std::cout ... args) std::endl; // 打印所有参数 } templatetypename... Args bool all_true(Args... args) { return (true ... args); // 检查所有参数是否为true }六、SFINAE与类型特性6.1 SFINAE替换失败不是错误// 检测类型是否有size()方法 templatetypename T auto has_size(const T t) - decltype(t.size(), std::true_type{}) { return std::true_type{}; } std::false_type has_size(...) { return std::false_type{}; } // 使用SFINAE选择函数重载 templatetypename T auto get_size(const T container, int) // 优先选择这个 - decltype(container.size(), size_t{}) { return container.size(); } templatetypename T size_t get_size(const T array, long) { // 后备版本 return sizeof(array) / sizeof(array[0]); } // 使用 std::vectorint vec(10); int arr[5]; get_size(vec, 0); // 调用第一个版本 get_size(arr, 0); // 调用第二个版本6.2 类型特性Type Traits// 移除const templatetypename T struct remove_const { using type T; }; templatetypename T struct remove_constconst T { using type T; }; // 使用 remove_constconst int::type x; // x的类型是int // 标准库中的类型特性 #include type_traits std::is_integralint::value; // true std::is_floating_pointdouble::value; // true std::is_pointerint*::value; // true std::is_referenceint::value; // true std::is_sameint, int::value; // true std::is_convertibleint, double::value; // true // enable_if的使用 templatetypename T typename std::enable_ifstd::is_integralT::value, bool::type is_odd(T value) { return bool(value % 2); } // 这个重载只对浮点数有效 templatetypename T typename std::enable_ifstd::is_floating_pointT::value, bool::type is_odd(T value) { return fmod(value, 2.0) ! 0.0; }七、概念与约束C207.1 概念Concepts// 定义概念 templatetypename T concept Integral std::is_integral_vT; templatetypename T concept FloatingPoint std::is_floating_point_vT; templatetypename T concept Addable requires(T a, T b) { { a b } - std::same_asT; }; // 使用概念约束函数 templateIntegral T T add_integral(T a, T b) { return a b; } templateFloatingPoint T T add_float(T a, T b) { return a b; } templateAddable T T add_any(T a, T b) { return a b; } // 使用requires子句 templatetypename T requires AddableT std::copyableT T add_with_requires(T a, T b) { return a b; } // 简写语法 Addable auto add_shorthand(Addable auto a, Addable auto b) { return a b; }7.2 概念应用示例// 要求容器有begin()和end() templatetypename Container concept Iterable requires(Container c) { c.begin(); c.end(); { *c.begin() } - std::same_astypename Container::value_type; }; // 改进的display函数 templateIterable Container void display_concept(const Container cont, std::ostream os std::cout) { for (const auto elem : cont) { os elem ; } os std::endl; } // 编译时检查 static_assert(Iterablestd::vectorint); // 通过 static_assert(Iterableint); // 错误int不可迭代八、模板元编程8.1 编译时计算// 编译时计算阶乘 templateunsigned n struct Factorial { static const unsigned long long value n * Factorialn-1::value; }; template struct Factorial0 { static const unsigned long long value 1; }; // 使用 constexpr auto fact5 Factorial5::value; // 120编译时计算 // 编译时计算斐波那契数列 templateunsigned n struct Fibonacci { static const unsigned value Fibonaccin-1::value Fibonaccin-2::value; }; template struct Fibonacci0 { static const unsigned value 0; }; template struct Fibonacci1 { static const unsigned value 1; };8.2 类型列表// 类型列表定义 templatetypename... Types struct TypeList {}; // 获取第N个类型 templatetypename List, unsigned N struct TypeAt; templatetypename Head, typename... Tail struct TypeAtTypeListHead, Tail..., 0 { using type Head; }; templatetypename Head, typename... Tail, unsigned N struct TypeAtTypeListHead, Tail..., N { using type typename TypeAtTypeListTail..., N-1::type; }; // 使用 using MyTypes TypeListint, double, std::string, char; typename TypeAtMyTypes, 1::type x; // x的类型是double九、高级模板技巧9.1 CRTP奇异递归模板模式// 静态多态 templatetypename Derived class Base { public: void interface() { static_castDerived*(this)-implementation(); } void implementation() { // 默认实现 std::cout Default implementation\n; } }; class Derived1 : public BaseDerived1 { public: void implementation() { std::cout Derived1 implementation\n; } }; class Derived2 : public BaseDerived2 { // 使用默认实现 }; // 使用 Derived1 d1; Derived2 d2; d1.interface(); // 输出: Derived1 implementation d2.interface(); // 输出: Default implementation9.2 标签分发// 标签类型 struct input_iterator_tag {}; struct output_iterator_tag {}; struct forward_iterator_tag : input_iterator_tag {}; struct bidirectional_iterator_tag : forward_iterator_tag {}; struct random_access_iterator_tag : bidirectional_iterator_tag {}; // 根据迭代器标签分派 templatetypename Iterator void advance_impl(Iterator it, typename std::iterator_traitsIterator::difference_type n, input_iterator_tag) { // 线性前进 while (n-- 0) it; } templatetypename Iterator void advance_impl(Iterator it, typename std::iterator_traitsIterator::difference_type n, random_access_iterator_tag) { // 随机访问 it n; } templatetypename Iterator void advance(Iterator it, typename std::iterator_traitsIterator::difference_type n) { using category typename std::iterator_traitsIterator::iterator_category; advance_impl(it, n, category{}); }十、模板的最佳实践10.1 模板代码组织// 头文件中声明和定义都在类内部 templatetypename T class MyClass { public: void method() { /* 实现 */ } }; // 或者头文件中分开声明和定义 templatetypename T class MyClass { public: void method(); }; templatetypename T void MyClassT::method() { /* 实现 */ } // 注意模板定义必须放在头文件中10.2 性能优化// 使用引用避免拷贝 templatetypename Container void process(const Container cont) { // const引用 for (const auto elem : cont) { // 再次使用引用 // ... } } // 完美转发 templatetypename T void set_value(T new_value) { // 万能引用 value std::forwardT(new_value); // 完美转发 } // 编译时ifC17 templatetypename T void process_value(T value) { if constexpr (std::is_integral_vT) { // 只在T是整数时编译 std::cout Integral: value * 2 \n; } else if constexpr (std::is_floating_point_vT) { // 只在T是浮点数时编译 std::cout Float: value * 1.5 \n; } else { // 其他类型 std::cout Other: value \n; } }10.3 错误处理// 静态断言 templatetypename T class OnlyForIntegrals { static_assert(std::is_integral_vT, OnlyForIntegrals只能用于整数类型); // ... }; // 概念约束C20 templatetypename T requires std::integralT class IntegralOnly { // 编译错误信息更友好 };总结C模板是一个强大的工具其核心要点泛型编程编写与类型无关的通用代码编译时多态通过模板实例化实现元编程在编译时进行计算和类型操作零开销抽象不产生运行时开销关键进步C11auto、decltype、可变参数模板C14返回类型推导、泛型lambdaC17折叠表达式、编译时if、类模板参数推导C20概念、requires子句、更多编译时功能掌握模板是深入理解现代C的关键它使得STL、Boost等库的实现成为可能是编写高性能、类型安全、可复用代码的基础。

更多文章