最简可用的函数模板是解决重复代码的刚需工具,只需用template声明类型参数,如template t max(t a, t b) { return a > b ? a : b; },注意类型必须支持对应运算、推导限制及定义须在头文件中。

怎么写一个最简可用的函数模板
函数模板不是“高级技巧”,而是解决重复代码的刚需工具。只要发现多个函数逻辑相同、只差类型,就该用 template。
基本写法就是把类型抽成参数,用 typename(或 class)声明占位符:
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
注意三点:
-
T必须能支持>运算,否则实例化时编译失败(不是定义时报错) - 调用时可显式指定
max<int>(3, 5)</int>,但多数情况编译器能自动推导,直接写max(3, 5)或max(3.14, 2.71)即可 - 不能把模板定义放在 .cpp 里单独编译——链接时会找不到实例,必须让声明和实现都在头文件中
为什么 std::swap 要特化,而你的模板不用
通用模板处理不了某些类型组合,比如 std::swap 对 std::vector 做的是资源交换(指针互换),而不是逐个拷贝元素。这是性能关键点。
立即学习“C++免费学习笔记(深入)”;
你自己的模板要不要特化,取决于是否遇到以下情况:
- 某类输入需要完全不同的算法(比如对
const char*比较要用strcmp,而非==) - 默认模板对某个类型无法编译(比如你写了
T::value,但int没这个成员) - 想禁用某类实例(如禁止
max<void></void>),可用static_assert或std::enable_if
特化写法示例(全特化):
template <>
void swap<int>(int& a, int& b) {
// 更快的整数交换,不依赖 std::swap
a ^= b ^= a ^= b;
}
模板参数推导失败的常见报错
最典型的是混合类型调用:max(3, 4.5)。编译器要同时推导 T 为 int 和 double,冲突,报错类似:
error: no matching function for call to 'max(int, double)'
解决方式有三种:
- 显式指定类型:
max<double>(3, 4.5)</double> - 提前转换一个参数:
max(3.0, 4.5) - 改用两个模板参数(但需谨慎,可能失去约束):
template <typename t typename u> auto max(T a, U b) -> decltype(a > b ? a : b)</typename>
第二种最常用,也最直观;第三种在 C++14 后可行,但返回类型推导依赖表达式语义,容易因隐式转换引入歧义。
函数模板和普通重载函数谁优先
当两者都匹配时,编译器优先选**非模板的重载函数**。这不是“模板优先级低”,而是标准规定的重载决议规则:非模板函数比模板实例更“特化”。
举个例子:
void foo(int x) { std::cout << "non-template\n"; }
template <typename T> void foo(T x) { std::cout << "template\n"; }
foo(42); // 输出 "non-template"
foo(3.14); // 输出 "template"
这个行为常被用来做“兜底”:先写几个高频类型的重载(int、const char*),再用模板覆盖其余类型。但要注意,重载集里如果有多个模板匹配,会触发二义性错误。
真正容易被忽略的是:模板的形参类型若带引用/const 修饰(如 T&),会影响推导结果,进而改变重载决议路径——这种细节在调试时很难一眼看出。










