模板优化 运用 function 及 外部模板
我们都知道模板是泛型的,但是,它一旦被实例化就会产生一个实例化的副本。
好了,大家应该能够猜到,低效模板和高效模板的差异了
一般的低效模板:
1.泛型实参表达形式多样导致的低效模板
2.多文件引用同一个模板形成的低效模板
我们接下来就分别来解决上述两种模板的低效问题
泛型实参表达形式多样导致的低效模板
模板头是使得泛型具体化的外交大使(泛型形式参数),但是,会出现这种现象:“相同的实例化类型,但是实参表达形式不同,而导致模板头每次都会为相同的泛型参数实例化一个副本,使得同时存在很多一模一样的实例化模板”。而我们正常情况下,一个实例化类型仅仅对应一个实例化副本即可。
比如说:
template<class T>
void f(const T& t)
{
static int time = ; cout << t << endl;
time++;
cout << "current time = " << time
<< " &time = " << &time << endl;
cout << endl;
} int main()
{
f();
f();
f(3.5);
f();
}
我们模板的实参自动推导为 int int double int ,当然,你也可以显示指定,如:f<int>(2)
f 函数实参为2 3 3.5 8
上述,我们创建的模板实例化副本有两个,分别为 void (*)(const int&)和void (*)(const double&)
我们调用了四次函数f
我们证明一下子

三个int 模板的time值都是延续的,地址都是一样的
而另外的double 模板的time和地址都是新的
但是,这只是很简单的类型,但是面对稍微高级一丢丢的表达式类型,模板则不会进行同类型判定,从而使得模板实例化同一个泛型实参导致多个实例化副本
如:
//test.h
#pragma once #include <iostream>
#include <functional> namespace templatespace
{
using std::cout;
using std::endl;
using std::function;
}; using namespace templatespace; template<class T, class F>
T fun(T v, F f)
{
static int count{ };
count++;
cout << "count = " << count
<< ", &count = " << &count << endl;
return f(v);
} class Fp
{
double z_;
public:
Fp(double z = 1.0) :z_(z) { }
double operator()(double p) { return z_*p; }
}; class Fq
{
double z_;
public:
Fq(double z = 1.0) :z_(z) { }
double operator()(double q) { return z_ + q; }
};
#include "test.h"
double couple(double x) { return 2.0*x; }
double square(double x) { return x*x; }
int main()
{
double t = 3.5;
cout << "use Function pointer couple:" << endl
<< " " << fun<double>(t, couple) << endl;
cout << "use Function pointer square:" << endl
<< " " << fun<double>(t, square) << endl;
cout << "use Function object Fq:" << endl
<< " " << fun<double>(t, Fq()) << endl;
cout << "use Function object Fp:" << endl
<< " " << fun<double>(t, Fp()) << endl;
cout << "use Lambda expression 1:" << endl
<< " " << fun<double>(t, [](double u) {return * u; }) << endl;
cout << "use Lambda expression 2:" << endl
<< " " << fun<double>(t, [](double u) {return u*u; }) << endl;
}
我们可以很明显地看到,主函数中两两一组,一共有三组
第一组中的第二个函数实参为一个函数指针
第二组中的第二个函数实参为一个函数对象
第三组中的第二个函数实参为一个lambda表达式
安排的明明白白的,这三组的第二个泛型参数实例化后均应为:double (*)(double)
即:它们都接受一个double型参数,然后返回一个double值,我们把这种性质称为特征标。
但是,模板操作起来并不是我们想的:

我们可以看到,只有第一组符合我们的期望,他们的count值是连续的,其量的地址也是相同的,说明这两个模板函数的实例化副本是同一个
那么我们如何让编译器,或者是模板实例化解析的时候将三组的第二个泛型实参接受为同一个类型呢
我们当然是要利用上述提到的特征标来将它们统一起来。
如:std::function<double(int, double)> ttt;
即:ttt对象代表着接受一个int 参数 和 double参数,并返回一个double参数
所以,我们将其运用到模板中 ,很容易的,我们会将代码改动成下面这样:
改动main.cpp
#include "test.h"
double couple(double x) { return 2.0*x; }
double square(double x) { return x*x; }
using fff = function<double(double)>;
int main()
{
double t = 3.5;
cout << "use Function pointer couple:" << endl
<< " 函数执行结果为:" << fun(t, fff(couple)) << endl << endl;
cout << "use Function pointer square:" << endl
<< " 函数执行结果为:" << fun(t, fff(square)) << endl << endl;
cout << "use Function object Fq:" << endl
<< " 函数执行结果为:" << fun(t, fff(Fq())) << endl << endl;
cout << "use Function object Fp:" << endl
<< " 函数执行结果为:" << fun(t, fff(Fp())) << endl << endl;
cout << "use Lambda expression 1:" << endl
<< " 函数执行结果为:" << fun(t, fff([](double u) {return * u; })) << endl << endl;
cout << "use Lambda expression 2:" << endl
<< " 函数执行结果为:" << fun(t, fff([](double u) {return u*u; })) << endl << endl;
}
也算是一种类型转换,或者更恰当的说是,抽取特征标。
当然运行结果也如我们所料

上述验证了,三组测试的第二个泛型参数的特征标是一样的,即他们的类型相同。、
当然,上述改动,有点花时间,我们可以在模板上面做改动,而无需主函数调用时每次修改:
template<class T>
T fun(T v, function<T(T)> f)
{
static int count{ };
count++;
cout << "count = " << count
<< ", &count = " << &count << endl;
return f(v);
}
而且,还省去了第二个泛型参数的自动推导
所以,经过上面的一系列动作,我们把三组测试数据,由原来的5份模板实例,优化到了一份模板实例。
多文件引用同一个模板形成的低效模板
但是我做了大量测试,仍没有看到上述所说的多份模板实例化副本的现象发生。
#pragma once
#include <iostream>
using std::cout;
using std::endl; template<class T>
void opera(const T& x)
{
static int count{ };
count++;
cout << "count = " << count
<< ", &count = " << &count << endl;
cout << "value : " << x << endl << endl;
} void test1();
test.h
#include "test.h" void test1()
{
opera(3.14);
}
test.cpp
#pragma once class test2
{
public:
test2(){}
void solve(double);
};
test2.h
#include "test2.h"
#include "test.h" void test2::solve(double x)
{
opera(x);
}
test2.cpp
#include "test.h"
#include "test2.h" void fun(double x)
{
opera(x);
} int main()
{
double r{ 3.14 };
test2 _2;
test1();
fun(r);
_2.solve(r);
}
main.cpp
上述描述中提到,创建两个cpp然后引用模板头文件,我创建了三个cpp,分别引用模板头文件,然后进行同类型泛型参数测试,结果:所有的模板实例化副本只有一个:

如有高见,请于下方留言,谢谢
模板优化 运用 function 及 外部模板的更多相关文章
- 模板函数(template function)出现编译链接错误(link error)之解析
总的结论: 将template function 或者 template class的完整定义直接放在.h文件中,然后加到要使用这些template function的.cpp文件中. 1. 现 ...
- javascript模板库jsrender加载并缓存外部模板文件
前一篇说了jsrender嵌套循环的使用,在SPA的应用中,广泛使用的一个点就是view模板,使用了SPA之后,每个业务页面不再是独立的html,仅仅是一个segment,所以通常这些segment会 ...
- groot 引入外部模板
index7.html <html><head> <title>groots引入外部模板van</title> <script src=" ...
- C++11外部模板
[C++11之外部模板] 在标准C++中,只要在编译单元内遇到被完整定义的模板,编译器都必须将其实例化(instantiate).这会大大增加编译时间,特别是模板在许多编译单元内使用相同的参数实例化. ...
- C++11 : 外部模板(Extern Template)
在C++98/03语言标准中,对于源代码中出现的每一处模板实例化,编译器都需要去做实例化的工作:而在链接时,链接器还需要移除重复的实例化代码.显然,让编译器每次都去进行重复的实例化工作显然是不必要的, ...
- vue组件化之模板优化及注册组件语法糖
vue组件化之模板优化及注册组件语法糖 vue组件化 模板 优化 在 https://www.cnblogs.com/singledogpro/p/12054895.html 这里我们对vue.js ...
- JavaScript模板引擎artTemplate.js——如何引入模板引擎?
artTeamplate.js在github上的地址:artTemplate性能卓越的js模板引擎 引入模板引擎,就是引入外部javascript啦,并且artTemplate.js不依赖其他第三方库 ...
- Win10系列:JavaScript 项目模板中的文件和项模板文件
通过上面内容的学习,相信读者已经对各种项目模板和项模板有了大致的了解,本节将进一步介绍项目模板中默认包含的项目文件以及项模板文件,首先讲解这些文件中的初始内容以及作用,然后介绍在一个页面中如何添加控件 ...
- 根据模板导出Excel报表并复制模板生成多个Sheet页
因为最近用报表导出比较多,所有就提成了一个工具类,本工具类使用的场景为 根据提供的模板来导出Excel报表 并且可根据提供的模板Sheet页进行复制 从而实现多个Sheet页的需求, 使用本工具类时 ...
随机推荐
- [整理]C语言中字符常量与ASCII码
所有的ASCII码都可以用“\”加数字(一般是8进制数字)来表示.而C中定义了一些字母前加"\"来表示常见的那些不能显示的ASCII字符,如\0,\t,\n等,就称为转义字符,因为 ...
- fifo 上使用 select -- 转
http://www.outflux.net/blog/archives/2008/03/09/using-select-on-a-fifo/ The right way to handle on-g ...
- JS比较两个数字大小
js不能直降比较两·个数大小,要先转化为整数再比较大小. parseInt()转化. 出处:http://www.jb51.net/article/98251.htm
- thinkphp 原数据更新
调用TP的save方法更新数据时,如果新数据与数据库中得数据一致, 那么执行M('table')->save(data)方法时,该方法会返回false.现在的需求是,哪怕用户要更新的数据与原数据 ...
- c++刷题(18/100)树
题目1:二叉搜索树的第k个节点 给定一颗二叉搜索树,请找出其中的第k小的结点.例如, 5 / \ 3 7 /\ /\ 2 4 6 8 中,按结点数值大小顺序第三个结点的值为4. 思路:因为是二叉搜索树 ...
- Python概念-禁锢术之__slots__
之所以给它起名为禁锢术,并非空缺来风,下面我们来了解一下__slost__ __slost__:其实就是将类中的名称锁定,实例化对象,只可以赋值和调用,不可以删除名字和增加新的名字 代码示例:(实例化 ...
- Groovy/Spock 测试导论
Groovy/Spock 测试导论 原文 http://java.dzone.com/articles/intro-so-groovyspock-testing 翻译 hxfirefox 测试对于软件 ...
- springcloud中eureka集群unavailable-replicas
unavailable-replicas 配置了集群,但是在注册中心显示另外的几个集群地址是不可用的: 1 首先需要再host中添加服务名映射,如果应映射了再看是否在yml中配置了prefer-ip- ...
- 洛谷 P3835: 【模板】可持久化平衡树
题目传送门:洛谷P3835. 题意简述: 题面说的很清楚了. 题解: 考虑建立一棵每个节点都表示一个版本的树. 以初始版本 \(0\) 为根.对于第 \(i\) 个操作,从 \(v_i\) 向 \(i ...
- shell脚本编程之“最简单的死循环”【转】
转自:http://blog.chinaunix.net/uid-23046336-id-3475462.html 在linux下编程的程序猿都知道shell脚本,就算你不怎么熟悉,也应该听过的吧!那 ...