乐于分享
好东西不私藏

现代C++学习材料-06 - 模板与泛型编程

现代C++学习材料-06 - 模板与泛型编程

🔴 难度:高级

1. 函数模板

// 基本函数模板template<typenameT>Tmax_val(Ta,Tb){return(a>b) ? a : b;}// 使用max_val(3,5);           // T = intmax_val(3.14,2.71);     // T = doublemax_val<std::string>("abc","xyz");  // 显式指定// C++20 Concepts 约束版本(更清晰)template<std::totally_orderedT>Tsafe_max(Ta,Tb){return(a>b) ? a : b;}// 简写形式(C++20)autosimple_max(std::totally_orderedautoa,std::totally_orderedautob){return(a>b) ? a : b;}

💡 为什么用模板?

泛型编程让算法独立于具体类型,同时保持零开销抽象——编译器为每种类型生成专门的代码。

2. 类模板

template<typename T,size_tMaxSize = 100>class Stack{public:void push(const T& value){if(size_>=MaxSize){throw std::overflow_error("Stack full");}data_[size_++] = value;}pop(){if(size_==0)throwstd::underflow_error("Stack empty");return data_[--size_];}const T& top()const{if(size_==0)throw std::underflow_error("Stack empty");return data_[size_-1];}[[nodiscard]]boolempty()const{return size_==0; }[[nodiscard]]size_tsize()const{return size_; }private:T data_[MaxSize];size_t size_ = 0;};// 使用Stack<int>s1;               // MaxSize = 100 (默认)Stack<double,50>s2;        // MaxSize = 50Stack<std::string>s3;       // 任意类型

3. 模板特化

// 通用版本template<typename T>class Printer{public:voidprint(constT& value)const{std::cout<<value<<"\n";}};// 全特化:针对 bool 类型template<>class Printer<bool>{public:voidprint(constbool& value)const{std::cout<<(value ? "true" : "false")<<"\n";}};// 偏特化:针对指针类型template<typename T>class Printer<T*>{public:voidprint(T* const& value)const{if(value)std::cout<< *value<<"\n";else std::cout<<"nullptr\n";}};

4. SFINAE 与 std::enable_if(C++11/17)

SFINAE(Substitution Failure Is Not An Error):模板参数替换失败不是错误,只是让这个特化版本退出候选集。

#include<type_traits>// C++11:enable_if 约束模板template<typename T>typename std::enable_if<std::is_integral_v<T>,T>::typeprocess(T value){return value*2;}template<typename T>typename std::enable_if<std::is_floating_point_v<T>,T>::typeprocess(T value){return value/2.0;}// C++17:if constexpr 大幅简化template<typename T>auto process_v2(T value){ifconstexpr(std::is_integral_v<T>){return value*2;}else if constexpr(std::is_floating_point_v<T>){return value/2.0;}}// C++20:Concepts 彻底取代 SFINAEtemplate<std::integral T>process_v3(T value){return value*2; }template<std::floating_point T>process_v3(T value){return value/2.0; }

💡 演进路径:SFINAE → if constexpr → Concepts。每一代都在降低模板编程的复杂度。

5. Concepts(C++20)—— 模板革命

#include<concepts>// 自定义 Concepttemplate<typename T>concept Printable = requires(T t){{std::cout<<t};  // 要求支持 << 运算符};template<typename T>concept Hashable = requires(T t){{std::hash<T>{}(t)} -> std::convertible_to<size_t>;};// 组合 Conceptstemplate<typename T>concept PrintableAndHashable = Printable<T>&&Hashable<T>;// 使用 Concept 约束voidlog(constPrintableauto& value){std::cout<<value<<"\n";}// requires 子句template<typename T>requires Printable<T>&&Hashable<T>void insert_into_cache(T value){auto hash = std::hash<T>{}(value);std::cout<<"Hash: "<<hash<<", Value: "<<value<<"\n";}

💡 Concepts 的价值:让模板约束变成可读的"接口文档",编译错误从几百行缩到一行。

6. 可变参数模板(Variadic Templates)

// C++11 递归展开template<typename T>autosum(T value){return value; }template<typename T,typename... Args>autosum(T first,Args... rest){return first+sum(rest...);}// C++17 折叠表达式(Fold Expression)—— 大幅简化!template<typename... Args>autosum_fold(Args... args){return(args+ ...);  // 一元右折叠}// 折叠表达式示例template<typename... Args>void print_all(Args... args){((std::cout<<args<<" "), ...);  // 逗号折叠std::cout<<"\n";}print_all(1,"hello",3.14,'x');  // 输出:1 hello 3.14 x// 实用示例:类型安全的 printftemplate<typename... Args>void safe_print(constchar* fmt,Args... args){(std::cout<< ... <<args);  // 折叠展开}

7. std::tuple 与模板技巧

#include<tuple>// tuple 是可变参数模板的经典应用auto person = std::make_tuple("Alice",25,3.8);  // string, int, double// C++17 结构化绑定解构 tupleauto[name,age,gpa] = person;std::cout<<name<<", "<<age<<", "<<gpa<<"\n";// tuple 实用技巧:编译期索引访问std::cout<<std::get<0>(person)<<"\n";  // "Alice"std::cout<<std::get<1>(person)<<"\n";  // 25// 实用:用 tuple 实现函数返回多个值std::tuple<int,std::string>divide(inta,intb){if(b==0)return{-1,"Division by zero"};return{a/b,"OK"};}auto[result,status] = divide(10,3);

✅ 本篇小结

模板是 C++ 泛型编程的基础,编译器在编译期为每种类型生成专门代码

  • SFINAE 是旧时代的约束手段,C++20 的 Concepts 全面取代它
  • 折叠表达式(C++17)让可变参数模板的展开变得极其简洁
  • Concepts 让模板编程从"黑魔法"变成了"可读的工程实践"

📖 下一篇:07-STL标准库