C++:typename 用法、函数指针传参与函数模板趣味实践(学习自用)

C++:typename 用法、函数指针传参与函数模板趣味实践(学习自用)

C++ 模板编程中,typename的精准使用、函数指针的参数传递、函数模板的类型自动推断,以及默认模板参数的灵活配置,是实现代码复用与泛型编程的核心能力。本文结合核心知识点,通过可运行代码示例,逐一拆解这些关键用法的原理与实践。

1.typename 的核心使用场合

typename是 C++ 模板编程的类型声明关键字,核心作用是明确 “后续标识符为类型”,主要有两大使用场景,且与class关键字存在本质区别。

1.1 模板参数声明:typename 与 class 的异同

在定义函数模板类模板时,typenameclass均可用于声明类型模板参数,功能完全一致,但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.总结

  1. typename:核心是声明类型,既用于模板参数声明,也用于解决::访问类型成员的二义性,是模板编程的基础关键字;
  2. 函数指针传参:通过typedef定义函数指针类型,传递函数名实现回调,是 C 语言风格的解耦方式;
  3. 函数模板趣味用法:利用类型自动推断兼容函数指针与可调用对象,结合默认参数实现极简调用,是 C++ 泛型编程的灵活体现;
  4. 默认模板参数:类模板需显式指定(空尖括号不可省),函数模板(C++11+)可结合推断简化调用,默认参数需遵循 “从后往前、连续指定” 的规则。
posted @ 2026-01-22 22:15  drink_all_day  阅读(2)  评论(0)    收藏  举报