最近因为想自己写一个信号槽,看到了一些开源代码,才发现,自己对模版的理解还是太浅了,理解了C++的模版才算真正入门了C++

1、说明

本文只针对技术提炼说明,不对使用场景做介绍。内容的理解需要在理解模板类(包括模板类的使用以及隐式/显式实例化)和C/C++函数指针以及stl函数对象的基础上

下面从以下几点讲解:

  1. 模板类传入C函数指针,函数参数固定;
  2. 模板类传入C++函数指针,函数参数固定;
  3. 模板类传入C函数指针,函数参数不固定;
  4. 模板类传入C++函数指针,函数参数不固定

2、模板类传入固定参数的C函数指针

先看一组示例:

template<typename T>
class Test;
template<typename F>
class Test<F(int)>
{
public:
typedef F(*Callback)(int); explicit Test(Callback func)
: callback(func)
{} void exec()
{
callback(999);
}
private:
Callback callback;
};

需要执行的函数:

int TestFunction(int value)
{
cout << value + 2 << endl;
return value;
}

调用示例:

int main()
{
Test<int(int)> test(TestFunction);
test.exec();
return 0;
}

执行结果:

1001

代码讲解:

template<typename F>
class Test<F(int)>
{}

理解一下上面这段代码做了什么,首先,这 是一个模板类的,并且它也是 模板类的显示实例化,既然是显示实例化,那么就需要模板类的声明,这就是下面第一二行代码的存在意义

template<typename T>
class Test;//模板类声明

这段可以这样理解,这里是先声明一个模板类 Test,它需要传入一个类型T;然后再显示实例化,显示实例化之后的类还是一个模板类,实例化的类需要传入的是一个函数指针(该函数指针就是第一个Test的T的显示实例化类型,即第一个Test的类型T被显示实例化为函数指针),该函数指针的返回值是类型F,参数有且只有一个int型,函数指针的声明如下:

typedef F(*Callback)(int);

所以,我们在使用的时候,需要指定,我们使用的是显示实例化的Test(这个Test类还是一个模板类,但是不同与前一个模板类Test),所以需要明确指定类型,如下所示:

Test<int(int)> test(TestFunction);

这行代码告诉编译器,我们使用的是显示实例化之后的模板类,另外需要再隐式实例化该模板类:

template<typename F>
class Test<F(int)>//隐式实例化,F类型为int
{}

然后使用构造函数,传入C函数指针 TestFunction,执行 exec() 方法的时候,则执行传进入的C函数

3、模板类传入固定参数的C++函数指针

示例代码如下:

template<typename T>
class Test;
template<typename F>
class Test<F(int)>
{
public:
typedef F(TestClass::*TestCallback)(int); explicit Test(TestClass *ptr, TestCallback func)
: ptr(ptr), testCallback(func)
{} void exec()
{
(*ptr.*testCallback)(777);
}
private:
TestCallback testCallback;
TestClass *ptr;
};

需要执行的C++类函数

class TestClass
{
public:
int TestFunction(int value)
{
cout << value + 2 << endl;
return value;
}
};

调用方:

int main()
{
TestClass testClass;
Test<int(int)> test(&testClass, &TestClass::TestFunction);
test.exec(); return 0;
}

执行结果:

779

代码说明:

模板类Test大致同第二节,只是这里的函数指针定义变成了类成员

typedef F(TestClass::*TestCallback)(int);

正因为如此,调用处不能只传一个函数指针,还需要类对象,所以构造函数需要两个变量,如下:

explicit Test(TestClass *ptr, TestCallback func)
: ptr(ptr), testCallback(func)
{}

执行函数这段使用类函数指针调用即可(看不懂的回去复习函数指针一文)

void exec()
{
(*ptr.*testCallback)(777);
}

3.1、用函数对象替代函数指针存储

上面代码分别出处了类对象和类函数指针,C++11中也可组合成函数对象,可以使得代码更直观,代码如下:

template<typename T>
class Test;
template<typename F>
class Test<F(int)>
{
public:
typedef F(TestClass::*TestCallback)(int); explicit Test(TestClass *ptr, TestCallback func)
{
testCall = std::bind(func, ptr, std::placeholders::_1);
} void exec()
{
testCall(888);
}
private:
std::function<F(int)> testCall;
};

代码说明:

这段代码的修改部分就是在构造函数中,不再存储函数指针和函数对象,而是利用C++11的std::bind组合成函数对象,代码的可读性更好,当然C函数也可以使用函数对象,这里不细说

4、模板类传入不定参数的C函数指针

第二节示例代码中传入的C函数是固定参数的函数,如果我们想灵活一点,传入不定参数,则需要做一些改动,示例代码如下:

template<typename T>
class Test;
template<typename F, typename... Args>
class Test<F(Args...)>
{
public:
typedef F(*Callback)(Args... args); explicit Test(Callback callback)
: callback(callback)
{} void exec()
{
callback(11, 22, 33);
}
private:
Callback callback;
};

需要执行的C++普通函数

template<typename... Args>
int TestFunction(Args... args)
{
std::list<int> arg_list = std::initializer_list<int>{args...};
while (!arg_list.empty())
{
cout << arg_list.front() << endl;
arg_list.pop_front();
}
return 0;
}

调用处:

Test<int(int, int, int)> test(TestFunction);
test.exec();

运行结果:

11
22
33

代码说明:

模板类Test需要做一些改动,指定参数类型即可

template<typename F, typename... Args>
class Test<F(Args...)>

调用出需要指定不定参数的类型和数量

Test<int(int, int, int)> test(TestFunction);//实例化模板

5、模板类传入不定参数的C++成员函数指针

和之前的demo不同,这里将上文的 TestClass 模板类型

template<typename M, typename T>
class Test;
template<typename M, typename F, typename... Args>
class Test<M, F(Args...)>
{
public:
typedef F(M::*TestCallback)(Args...); explicit Test(M *ptr, TestCallback func)
: ptr(ptr), testCallback(func)
{
} void exec()
{
(*ptr.*testCallback)(11, 22, 33);
}
private:
TestCallback testCallback;
M *ptr;
};

需要执行的类方法

template<typename ...Args>
class TestClass
{
public:
int TestFunction(Args... args)
{
std::list<int> arg_list = std::initializer_list<int>{args...};
while (!arg_list.empty())
{
cout << arg_list.front() << endl;
arg_list.pop_front();
}
return 0;
}
};

调用处

template<typename... Args>
void func()
{
using TC = TestClass<Args...>;
TC testClass;
Test<TC, int(Args...)> test(&testClass, &TC::TestFunction);
test.exec();
} int main()
{
func<int, int, int>();//实例化 return 0;
}

C/C++可变参数模版和函数指针的结合的更多相关文章

  1. 你必须知道的指针基础-7.void指针与函数指针

    一.不能动的“地址”—void指针 1.1 void指针初探 void *表示一个“不知道类型”的指针,也就不知道从这个指针地址开始多少字节为一个数据.和用int表示指针异曲同工,只是更明确是“指针” ...

  2. C++基础——函数指针 函数指针数组

    ==================================声明================================== 本文版权归作者所有. 本文原创,转载必须在正文中显要地注明 ...

  3. 【实习记】2014-08-27堆排序理解总结+使用typedef指代函数指针

        过程记录 4个月前C语言版的七大排序算法实践让我在写C++版时轻车熟路.特别是冒泡,插入,希尔,选择这四种排序不用调试即运行成功.输出的效果与C语言做的版本完全一样,其中令我印象深刻的是,co ...

  4. C/C++中的函数指针的使用与总结

    概要: 函数指针介绍 typedef简化函数指针的定义 指向函数的指针的初始化和赋值 通过指针调用函数 函数指针形参 返回指向函数的指针 指向重载函数的指针 参考<C++ Primer> ...

  5. 函数指针的理解 from 数据结构

    今天在学习数据结构中遇到一些问题,函数的指针不知道怎么用,给自己科普一哈 1 int LocateElem_Sq(SqList L, LElemType_Sq e, Status(*Compare)( ...

  6. 成员函数指针与高性能C++委托

    1 引子 标准C++中没有真正的面向对象的函数指针.这一点对C++来说是不幸的,因为面向对象的指针(也叫做“闭包(closure)”或“委托(delegate)”)在一些语言中已经证明了它宝贵的价值. ...

  7. c :函数指针具体解释

    在研究opencv源码的过程中.处处可见到函数指针,于是翻出来谭浩强的<C程序设计>把函数指针这一块内容再补一补! 1 定义 数据类型 (*指针变量名)(參数表); 注: 数据类型是指的函 ...

  8. [转]成员函数指针与高性能的C++委托

    原文(作者:Don Clugston):Member Function Pointers and the Fastest Possible C++ Delegates 译文(作者:周翔): 成员函数指 ...

  9. 使用WSAIoctl获取AcceptEx函数指针 [转]

    Winsock2的其他供应商不一定会实现AcceptEx函数.同样情况也包括的其他Microsoft的特定APIs如TransmitFile,GetAcceptExSockAddrs以及其他Micro ...

  10. C/C++学习:函数指针

    曾经在书上看到函数指针相关的都没怎么重视.可是近期在实际的工作中却派上了用场.所以认真地学习了一遍. 函数指针的申明 申明一个函数指针非常easy,就是将函数申明中的函数名替换为一个指针就可以: C/ ...

随机推荐

  1. Python | __init__.py的神奇用法

    0._init_.py 在Python工程里,当python检测到一个目录下存在_init_.py文件时,python就会把它当成一个模块(module).Module跟C++的命名空间和Java的P ...

  2. 机器学习 | 分类性能度量指标 : ROC曲线、AUC值、正确率、召回率

    本篇博客的图源来自 zhwhong,转载仅作学习使用! 在分类任务中,人们总是喜欢基于错误率来衡量分类器任务的成功程度.错误率指的是在所有测试样例中错分的样例比例.实际上,这样的度量错误掩盖了样例如何 ...

  3. 密集计算场景下的 JNI 实战

    作者:vivo 互联网服务器团队- Wei Qianzi.Li Haoxuan 在 Java 发展历程中,JNI 一直都是一个不可或缺的角色,但是在实际的项目开发中,JNI 这项技术应用的很少.在笔者 ...

  4. 【体验有奖】玩转 AIGC,函数计算 x 通义千问预体验,一键部署AI应用赢Airpods

    玩转 AIGC,基于函数计算 FC 部署通义千问预体验 从文字生成到图片生成,AIGC 的创造力让人惊叹,更多的人开始探索如何使用 AI 提高生产效率,激发更多创作潜能,然而在实际应用中,AI 技术的 ...

  5. 面试重点:webpack

    webpack 熟练掌握Webpack的常用配置,能够自己构建前端环境,并进行项目优化; 001.谈谈你对webpack的看法: webpack是一个模块打包工具,可以使用它管理项目中的模块依赖,并编 ...

  6. ECharts大屏数据可视化展板项目 适配rem

    1.在utils文件夹里新建一个rem.js 2.main.js中引入rem.js 3.vscode中下载cssrem插件,配置Root Font Size大小,为1920/20 = 96. 重启vs ...

  7. zzuli 1907: 小火山的宝藏收益

    ***题意:中文的 做法:邻接表+DFS,就相当于搜一棵树,比较一下当前结点得到的宝藏多还是子树下面得到的宝藏多,仔细想想就是水题*** #include<iostream> #inclu ...

  8. poj 2533 LIS(最长上升序列)

    ***一道裸题, 思路:在g数组内往里加元素,一直扩大这个数组,每次查询的时候,用二分查找,时间复杂度O(nlog(n)) *** #include<iostream> #include& ...

  9. python 基础 | 实现微秒级计时

    搬运一个计时代码: import datetime s = datetime.datetime.now() # 开始 # do something e = datetime.datetime.now( ...

  10. Redis 缓存常见问题

    本文为博主原创,未经允许不得转载: 目录: 1. 缓存穿透 1.1 出现原因 1.2 解决方案 1.3 布隆过滤器 2. 缓存雪崩 3. 缓存失效(缓存击穿,热点缓存) 1. 缓存穿透: 缓存穿透是指 ...