
C++ 作为一门静态类型语言,通过函数重载(Function Overloading)和模板(Templates)提供了强大的多态机制。函数重载允许同名函数根据参数列表的不同实现不同行为,而模板则实现了代码的泛型化。当两者结合时,即函数模板重载(Function Template Overloading),会带来更灵活的代码设计,但也伴随着复杂的匹配规则。
函数重载是指在同一作用域内定义多个同名函数,但它们的参数列表(参数类型、数量或顺序)必须不同。编译器会根据调用时的实参类型和数量选择最合适的函数版本。
// 函数重载示例
void print(int x) {
std::cout << "Integer: " << x << std::endl;
}
void print(double x) {
std::cout << "Double: " << x << std::endl;
}
void print(const char* s) {
std::cout << "String: " << s << std::endl;
}
int main() {
print(42); // 调用print(int)
print(3.14); // 调用print(double)
print("Hello"); // 调用print(const char*)
return 0;
}
①参数列表必须不同:仅返回类型不同不足以构成重载
int add(int a, int b); // 正确
double add(int a, int b); // 错误:仅返回类型不同②作用域必须相同:不同作用域内的同名函数不构成重载
void func(int x) {} // 全局作用域
namespace N {
void func(double x) {} // 命名空间N内,不与全局func重载
}③const 重载:const 修饰的成员函数可以与非 const 版本重载
class MyClass {
public:
void func() {} // 非const版本
void func() const {} // const版本,构成重载
};函数模板是一种通用的函数定义,它使用模板参数来表示类型或值的占位符。编译器会根据调用时的实参类型自动实例化具体的函数版本。
// 函数模板示例
template <typename T>
T max(T a, T b) {
return a > b ? a : b;
}
int main() {
int x = max(3, 5); // 实例化为max<int>(int, int)
double y = max(3.14, 2.71); // 实例化为max<double>(double, double)
return 0;
}①类型参数:使用typename或class声明
template <typename T>
T add(T a, T b) { return a + b; }②非类型参数:表示具体的值(如整数、指针等)
template <int N>
void printN() { std::cout << N << std::endl; }③模板模板参数:参数本身是一个模板
template <template <typename> class Container>
void printContainer(const Container<int>& c) { ... }函数模板本身并不是函数,而是一个用于生成函数的蓝图。当编译器遇到对函数模板的调用时,它会根据传入的实参类型推演出模板参数的实际类型,并生成一个针对该类型的函数实例。这个过程称为模板的实例化。
#include <iostream>
using namespace std;
template <typename T>
T add(T a, T b) {
return a + b;
}
int main() {
int sum1 = add(3, 5); // 实例化为int版本
double sum2 = add(3.14, 2.71); // 实例化为double版本
cout << "整数和: " << sum1 << endl;
cout << "浮点数和: " << sum2 << endl;
return 0;
}
函数模板重载是指定义多个同名的函数模板,它们的模板参数列表或函数参数列表不同。编译器在调用时需要同时考虑模板实例化和重载解析两个过程。
// 函数模板重载示例
template <typename T>
void print(T x) {
std::cout << "Template 1: " << x << std::endl;
}
template <typename T>
void print(T* x) {
std::cout << "Template 2: " << *x << std::endl;
}
int main() {
int a = 42;
print(a); // 调用Template 1
print(&a); // 调用Template 2(更匹配指针参数)
return 0;
}
①模板参数列表不同:
template <typename T>
void func(T a) {} // 模板1
template <typename T, typename U>
void func(T a, U b) {} // 模板2,参数数量不同②函数参数列表不同:
template <typename T>
void func(T a) {} // 模板1
template <typename T>
void func(T* a) {} // 模板2,参数类型不同(指针)③模板特化与重载:
template <typename T>
void func(T a) {} // 主模板
template <>
void func<int>(int a) {} // 全特化,不构成重载
template <typename T>
void func(T* a) {} // 新的函数模板,构成重载当调用一个重载函数或函数模板时,编译器按以下步骤确定最佳匹配:
char到int)int到double)...)// 函数模板重载示例
template <typename T>
void func(T a) {
std::cout << "Template 1: T" << std::endl;
}
template <typename T>
void func(T* a) {
std::cout << "Template 2: T*" << std::endl;
}
void func(int a) {
std::cout << "Non-template: int" << std::endl;
}
int main() {
int x = 42;
func(x); // 调用Non-template: int(精确匹配)
func(&x); // 调用Template 2: T*(精确匹配)
func(3.14); // 调用Template 1: T(实例化为func<double>)
return 0;
}
①模板参数推导:编译器会尝试根据实参类型推导模板参数
template <typename T>
void func(T a, T b) {} // 需要两个相同类型的参数
func(1, 2); // 正确:T推导为int
func(1, 3.14); // 错误:无法一致推导T(int vs double)②显式模板实参:当推导失败时,可显式指定模板参数
template <typename T>
T max(T a, T b) { return a > b ? a : b; }
max<int>(1, 3.14); // 正确:显式指定T为int③非类型模板参数:必须是编译时常量
template <int N>
void print() { std::cout << N << std::endl; }
int x = 10;
print<x>(); // 错误:x不是编译时常量
print<10>(); // 正确当普通函数与函数模板实例化产生竞争时,编译器优先选择普通函数:
// 普通函数
void print(const char* s) {
std::cout << "Non-template: " << s << std::endl;
}
// 函数模板
template <typename T>
void print(T s) {
std::cout << "Template: " << s << std::endl;
}
int main() {
print("Hello"); // 调用普通函数(精确匹配)
print(42); // 调用模板函数(实例化为print<int>)
return 0;
}
当多个函数模板都能匹配时,编译器选择更特化的模板:
// 通用模板
template <typename T>
void func(T a) {
std::cout << "General template" << std::endl;
}
// 更特化的模板(针对指针)
template <typename T>
void func(T* a) {
std::cout << "Pointer template" << std::endl;
}
int main() {
int x = 42;
func(x); // 调用通用模板
func(&x); // 调用指针模板(更特化)
return 0;
}
函数模板特化不会参与重载解析,而是替换已选择的模板实例:
// 主模板
template <typename T>
void func(T a) {
std::cout << "Primary template" << std::endl;
}
// 全特化
template <>
void func<int>(int a) {
std::cout << "Specialization for int" << std::endl;
}
int main() {
func(42); // 调用特化版本
func(3.14); // 调用主模板(实例化为double)
return 0;
}
#include <iostream>
#include <vector>
#include <algorithm>
// 通用模板:对数组排序
template <typename T, size_t N>
void sort(T (&arr)[N]) {
std::sort(arr, arr + N);
std::cout << "Sorted array" << std::endl;
}
// 特化版本:对char数组排序并转为大写
template <>
void sort<char, 10>(char (&arr)[10]) {
std::sort(arr, arr + 10);
for (char& c : arr) {
c = std::toupper(c);
}
std::cout << "Sorted and capitalized char array" << std::endl;
}
// 重载版本:对vector排序
template <typename T>
void sort(std::vector<T>& vec) {
std::sort(vec.begin(), vec.end());
std::cout << "Sorted vector" << std::endl;
}
int main() {
int arr1[5] = {5, 3, 1, 4, 2};
sort(arr1); // 调用通用模板
char arr2[10] = {'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'};
sort(arr2); // 调用特化版本
std::vector<double> vec = {3.14, 1.59, 2.65};
sort(vec); // 调用重载版本
return 0;
}
①模板参数推导失败:
template <typename T>
void func(std::vector<T> vec) {}
func({1, 2, 3}); // 错误:无法推导T
func(std::vector<int>{1, 2, 3}); // 正确②特化与重载混淆:
template <typename T>
void func(T a) {} // 主模板
template <>
void func(int* a) {} // 错误:特化参数不匹配主模板
template <typename T>
void func(T* a) {} // 正确:新的重载模板③重载歧义:
template <typename T>
void func(T a) {}
template <typename T>
void func(T* a) {}
int* p = nullptr;
func(p); // 正确:调用func(T*)
func(nullptr); // 错误:歧义(nullptr可转换为T*或std::nullptr_t)template <typename T>
void func(T&& arg) { // 万能引用
// 使用std::forward实现完美转发
}template <typename T>
constexpr bool is_pointer_v = std::is_pointer<T>::value;template <typename... Args>
auto sum(Args... args) {
return (args + ...); // 折叠表达式
}
template <typename T>
void process(T t) {
if constexpr (std::is_integral_v<T>) {
// 整数类型处理
} else {
// 其他类型处理
}
}template <typename T>
requires std::integral<T>
T add(T a, T b) {
return a + b;
}
// 等价写法
template <std::integral T>
T add(T a, T b) {
return a + b;
}函数重载和模板是 C++ 中实现多态和泛型编程的重要工具,当两者结合时,可以创建出既灵活又高效的代码。理解函数匹配规则和模板实例化过程是掌握这一技术的关键:
合理使用函数重载和模板重载,可以使代码更加简洁、灵活和可维护。但也要注意避免常见陷阱,如模板参数推导失败、重载歧义和特化冲突等问题。随着 C++ 标准的不断发展,如概念(Concepts)等新特性的引入,函数重载和模板的使用将变得更加安全和直观。