模板特化(Template Specialization)
模板特化允许开发者为特定类型或类型组合提供专门的实现。当通用模板无法满足特定需求时,特化模板可以调整行为以处理特定的情况。C++ 支持全特化(Full Specialization)*******_和__***_偏特化(Partial Specialization),但需要注意的是,函数模板不支持偏特化,只能进行全特化
类模板的特例化
类模板全特化
template<typename T>
class Printer {
public:
void print(const T& obj) {
std::cout<<"General Printer "<<obj<<std::endl;
}
};
template<>
class Printer<std::string> {
public:
void print(const std::string& obj) {
std::cout<<"String Printer "<<obj<<std::endl;
}
};
类模板偏特化
template<class T, class U>
class Pair {
public:
T first;
U second;
void print_pair() {
std::cout<<first<<" "<<second<<std::endl;
}
};
template<typename T, typename U>
class Pair<T, U*> {
public:
T first;
U* second;
void print() {
std::cout<<first<<" "<<*second<<std::endl;
}
};
调用实例
double temp = 1.1;
Pair<int, double*> pair(20, &temp);
pair.print();
编译器会优先匹配特例化的模板,再匹配普通模板,如果这里没有实现这个特例化,传指针会打印这个指针指向的地址。
函数模板的特例化
与类模板不同,函数模板不支持偏特化,只能进行全特化。当对函数模板进行全特化时,需要显式指定类型。
代码实例
template<typename T>
void printValue(const T& obj) {
std::cout<<"General Printer "<<obj<<std::endl;
}
template<>
inline void printValue<std::string>(const std::string& value) {
std::cout<<"String Printer "<<value<<std::endl;
}
template<>
inline void printValue<int*>(int* const& value) {
std::cout<<"int* Printer "<<*value<<std::endl;
}
需要注意的问题
一、 这里不加inline会报错重定义
在 C++ 中,模板(包括特化模板)的实现代码如果直接写在头文件(.h)中,且该头文件被多个 .cpp 文件包含,会导致编译器在每个 .cpp 中都生成一份函数定义,最终链接时出现 “重复定义” 冲突。
解决方案
-
将特化函数标记为
inline在函数前加
inline,强制编译器合并重复定义:template<> inline void printValue<std::string>(const std::string& value) { std::cout << "String Printer " << value << std::endl; } -
将特化实现放在
.cpp文件中头文件中仅声明特化,实现写在对应的
.cpp(如template.cpp)中:// 头文件 template.h template<> void printValue<std::string>(const std::string& value); // 实现文件 template.cpp #include "template.h" template<> void printValue<std::string>(const std::string& value) { std::cout << "String Printer " << value << std::endl; }
二、为什么写int* const&
模板原声明是 void printValue(const T& obj),当 T=int* 时:
const T& 等价于 int* const&(“指向 int 的指针的常量引用”),而非 const int*&(“指向常量 int 的指针的引用”)。
如果你的实际需求是接收 “指向常量 int 的指针”,则应将特化的 T 定义为 const int*,此时 const T& 等价于 const int* const&,特化代码如下:
// 特化 T=const int*
template<>
inline void printValue<const int*>(const int* const& value) {
std::cout << "const int* Printer " << *value << std::endl;
}
变参模板(Variadic Templates)
变参模板允许模板接受可变数量的参数,提供极高的灵活性,是实现诸如 std::tuple、std::variant 等模板库组件的基础。
定义与语法
变参模板使用 参数包(Parameter Pack),通过 ...语法来表示。
语法:
template <typename... Args>
class MyClass { /* ... */ };
template <typename T, typename... Args>
void myFunction(T first, Args... args) { /* ... */ }
变参模板实现方法
一、递归实现
inline void printAll() {
std::cout<<std::endl;
}
template<typename T,typename... Args>
inline void printAll(const T& first, Args... args) {
std::cout<<first<<" ";
printAll(args...);
}
- 无参版本:作为递归的终止条件,仅输出一个换行;
- 变参模板版本:接收 “第一个参数 + 剩余参数包”,先打印第一个参数,再递归调用自身处理剩余参数,直到参数包为空,触发无参版本
二、折叠表达式(C++17以后)
template<typename... Args>
void coutAll(const Args&... args) {
((std::cout<<args<<" "), ...);
std::cout<<std::endl;
}
折叠表达式 ((std::cout<<args<<" "), ...)
-
语法解析:
...是折叠运算符,作用是 “展开参数包args”;- 外层
()是折叠表达式的语法要求,内层()是逗号表达式的要求; - 逗号运算符
,的特性:按顺序执行左侧表达式,最终返回右侧表达式结果(这里只利用 “顺序执行” 的特性,忽略返回值)。
-
执行过程(以
coutAll(10, 3.14, "hello")为例):表达式会被编译器展开为:
(std::cout<<10<<" "), (std::cout<<3.14<<" "), (std::cout<<"hello"<<" ");即依次执行每个参数的打印操作,实现参数包的遍历。
折叠表达式示例
template<typename... Args>
auto sumAll(Args... args)->decltype((args + ...)) {
return (args + ...);
}
decltype可以理解为 “类型探测器”:给它一个表达式,它能精准返回这个表达式的类型,哪怕是复杂的、无法手动写出来的类型(比如模板推导的类型、函数返回值类型),decltype的核心是编译期推导表达式的原始类型,保留const、引用、指针等所有属性。
当函数返回值类型依赖于参数类型,且无法用auto直接推导时,decltype能精准匹配。
1194

被折叠的 条评论
为什么被折叠?



