模板优化 运用 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页的需求, 使用本工具类时 ...
随机推荐
- spring——获取ClassLoader
org.springframework.util包下的ClassUtils类有个静态方法:getDefaultClassLoader() 可以获取当前类加载器,如下: public static Cl ...
- 【BZOJ】2208 [Jsoi2010]连通数
[题意]给定n个点的有向图,求可达点对数(互相可达算两对,含自身).n<=2000. [算法]强连通分量(tarjan)+拓扑排序+状态压缩(bitset) [题解]这题可以说非常经典了. 1. ...
- HDU 4521 小明系列问题——小明序列 (线段树 单点更新)
题目连接 Problem Description 大家都知道小明最喜欢研究跟序列有关的问题了,可是也就因为这样,小明几乎已经玩遍各种序列问题了.可怜的小明苦苦地在各大网站上寻找着新的序列问题,可是找来 ...
- sklearn评估模型的方法
一.acc.recall.F1.混淆矩阵.分类综合报告 1.准确率 第一种方式:accuracy_score # 准确率import numpy as np from sklearn.metrics ...
- 排序算法的java实现
冒泡.选择就不写了.很常见 一:插入排序: /** * 插入排序 */ public class P4_3 { static void insertSort(int[] a){ int j,t; /* ...
- Virut样本取证特征
1.网络特征 ant.trenz.pl ilo.brenz.pl 2.文件特征 通过对文件的定位,使用PEID查看文件区段,如果条件符合增加了7个随机字符区段的文件,则判定为受感染文件. 3.受感染特 ...
- 41 - 数据库-pymysql41 - 数据库-pymysql-DBUtils
目录 1 Python操作数据库 2 安装模块 3 基本使用 3.1 创建一个连接 3.2 连接数据库 3.3 游标 3.3.1 利用游标操作数据库 3.3.2 事务管理 3.3.3 执行SQL语句 ...
- 【HASPDOG】Communication error
靠,防火墙没关,关了防火墙生成文件成功
- Jenkins无法安装插件或首次安装插件界面提示Offline
一.首先点击系统管理 二.点击插件管理 三.选择高级管理 四.将升级站点中的https改成http即可
- mysql高可用架构 -> MHA主从复制-03
GTID复制技术说明 GTID的全称为 global transaction identifier ,可以翻译为全局事务标示符,GTID在原始master上的事务提交时被创建.GTID需要在全局的主- ...