C++:typename 用法、函数指针传参与函数模板趣味实践(学习自用)
C++:typename 用法、函数指针传参与函数模板趣味实践(学习自用)
C++ 模板编程中,typename的精准使用、函数指针的参数传递、函数模板的类型自动推断,以及默认模板参数的灵活配置,是实现代码复用与泛型编程的核心能力。本文结合核心知识点,通过可运行代码示例,逐一拆解这些关键用法的原理与实践。
1.typename 的核心使用场合
typename是 C++ 模板编程的类型声明关键字,核心作用是明确 “后续标识符为类型”,主要有两大使用场景,且与class关键字存在本质区别。
1.1 模板参数声明:typename 与 class 的异同
在定义函数模板或类模板时,typename和class均可用于声明类型模板参数,功能完全一致,但typename更直观地表达 “后续是类型” 的语义。
- 函数模板声明:
template <typename T, int b> int func2(T c[]) - 类模板声明:
template <typename T> class myvector
关键区别:typename仅用于模板中声明类型参数,而class在模板外是定义类的关键字,二者在非模板场景下含义完全不同,不可混淆。
1.2 标明类型成员:解决::的二义性
当通过作用域运算符::访问类的类型成员(如typedef定义的迭代器、嵌套类型)时,编译器无法直接区分::后是类型还是静态成员变量 / 函数,此时必须用typename显式声明 “后续是类型”,避免编译歧义。
代码示例:
#include <iostream>
using namespace std;
// 类模板:定义迭代器类型成员
template <typename T>
class myvector {
public:
using iterator = T*; // 类型成员(迭代器)
iterator mybegin(); // 迭代器接口声明
};
// 类外实现成员函数:必须用typename标明myvector<T>::iterator是类型
template <typename T>
typename myvector<T>::iterator myvector<T>::mybegin() {
return nullptr; // 模拟迭代器起始位置
}
int main() {
myvector<int> vec;
vec.mybegin(); // 调用迭代器接口
return 0;
}
原理:编译器处理模板时,myvector<T>::iterator可能是类型,也可能是静态成员,typename强制编译器将其解析为类型,消除二义性。
2.函数指针作为函数参数的传递
函数指针是 C/C++ 实现回调机制的核心,可将函数作为参数传递给其他函数,实现逻辑解耦。
2.1 定义函数指针类型
通过typedef定义函数指针类型,明确函数的返回值和参数列表,简化代码书写:
// 定义函数指针类型:返回int,参数为两个int
typedef int (*FunType)(int, int);
2.2 函数指针传参与调用
定义接收函数指针的函数,直接传递函数名(函数名本质是函数地址)作为参数,通过函数指针调用目标函数。
完整示例:
#include <iostream>
using namespace std;
// 目标函数:计算两数之和
int add(int a, int b) {
return a + b;
}
// 定义函数指针类型
typedef int (*FunType)(int, int);
// 接收函数指针的函数
void testfunc(int i, int j, FunType funcpoint) {
// 通过函数指针调用目标函数
int result = funcpoint(i, j);
cout << "函数指针调用结果:" << result << endl;
}
int main() {
// 传递函数名add作为参数(函数名即函数地址)
testfunc(3, 4, add); // 输出:函数指针调用结果:7
return 0;
}
3.函数模板的趣味用法:兼容函数指针与可调用对象
函数模板的类型自动推断特性,可让同一个模板同时处理函数指针和可调用对象(重载()运算符的类对象),实现 “一套代码,多种调用” 的趣味效果。
3.1 可调用对象的定义
可调用对象是重载了()运算符的类对象,使用时可像调用函数一样,通过对象名(参数)的方式执行逻辑。
// 可调用对象类:重载()运算符实现两数相加
class tc {
public:
tc() { cout << "tc构造函数执行" << endl; }
tc(const tc&) { cout << "tc拷贝构造函数执行" << endl; }
// 重载()运算符:核心逻辑
int operator()(int v1, int v2) const {
return v1 + v2;
}
};
3.2 函数模板自动推断类型
将接收函数指针的函数改为函数模板,编译器会自动推断模板参数类型,兼容函数指针和可调用对象:
// 函数模板:兼容函数指针和可调用对象
template <typename T, typename F>
void testfunc(const T& i, const T& j, F funcpoint) {
cout << "模板调用结果:" << funcpoint(i, j) << endl;
}
int main() {
// 1. 传递函数指针(add):编译器推断F为FunType
testfunc(3, 4, add);
// 2. 传递可调用对象(tc临时对象):编译器推断F为tc类型
testfunc(3, 4, tc());
return 0;
}
运行结果:
模板调用结果:7
tc构造函数执行
模板调用结果:7
3.3 模板参数默认值的趣味应用
C++11 后支持函数模板默认参数,可给模板参数指定默认类型 / 值,进一步简化调用:
// 函数模板:指定默认模板参数F=tc,默认函数参数为tc()
template <typename T, typename F = tc>
void testfunc_default(const T& i, const T& j, F funcpoint = F()) {
cout << "默认参数调用结果:" << funcpoint(i, j) << endl;
}
int main() {
// 仅传两个参数,使用默认模板参数F=tc,默认参数tc()
testfunc_default(3, 4); // 输出:tc构造函数执行 → 默认参数调用结果:7
return 0;
}
关键规则:
- 模板默认参数需从后往前指定,且必须连续;
- 函数参数默认值需与模板默认参数匹配,确保可调用性;
- 若显式传递参数,默认参数将失效。
4.默认模板参数详解
默认模板参数允许为模板参数指定默认值,分为类模板默认参数和函数模板默认参数(C++11 及以上支持),大幅简化模板实例化。
4.1 类模板的默认参数
类模板必须显式指定模板参数(编译器无法自动推断),默认参数可简化实例化过程:
// 类模板:指定默认类型T=string,默认非类型参数size=5
template <typename T = string, int size = 5>
class myarray {
private:
T arr[size];
public:
void showSize() { cout << "myarray大小:" << size << endl; }
};
int main() {
// 1. 使用全部默认参数(空尖括号不可省略)
myarray<> arr1;
arr1.showSize(); // 输出:myarray大小:5
// 2. 部分指定默认参数
myarray<int> arr2; // T=int,size=5
arr2.showSize(); // 输出:myarray大小:5
myarray<int, 10> arr3; // T=int,size=10
arr3.showSize(); // 输出:myarray大小:10
return 0;
}
4.2 函数模板的默认参数
C++11 前仅类模板支持默认参数,C++11 后函数模板也支持,结合类型推断可实现极简调用:
#include <iostream>
using namespace std;
int add(int a, int b) { return a + b; }
typedef int (*FunType)(int, int);
// 函数模板:指定默认模板参数F=FunType,默认函数参数=add
template <typename T, typename F = FunType>
void testfunc_func_default(const T& i, const T& j, F funcpoint = add) {
cout << "函数模板默认参数调用:" << funcpoint(i, j) << endl;
}
int main() {
testfunc_func_default(3, 4); // 仅传两个参数,使用默认add函数
return 0;
}
5.总结
- typename:核心是声明类型,既用于模板参数声明,也用于解决
::访问类型成员的二义性,是模板编程的基础关键字; - 函数指针传参:通过
typedef定义函数指针类型,传递函数名实现回调,是 C 语言风格的解耦方式; - 函数模板趣味用法:利用类型自动推断兼容函数指针与可调用对象,结合默认参数实现极简调用,是 C++ 泛型编程的灵活体现;
- 默认模板参数:类模板需显式指定(空尖括号不可省),函数模板(C++11+)可结合推断简化调用,默认参数需遵循 “从后往前、连续指定” 的规则。
浙公网安备 33010602011771号