我们都知道模板是泛型的,但是,它一旦被实例化就会产生一个实例化的副本。

好了,大家应该能够猜到,低效模板和高效模板的差异了

一般的低效模板:

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份模板实例,优化到了一份模板实例。

多文件引用同一个模板形成的低效模板

情况描述大概是这样的:传送门  、传送门2

但是我做了大量测试,仍没有看到上述所说的多份模板实例化副本的现象发生。

#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 及 外部模板的更多相关文章

  1. 模板函数(template function)出现编译链接错误(link error)之解析

    总的结论:    将template function 或者 template class的完整定义直接放在.h文件中,然后加到要使用这些template function的.cpp文件中. 1. 现 ...

  2. javascript模板库jsrender加载并缓存外部模板文件

    前一篇说了jsrender嵌套循环的使用,在SPA的应用中,广泛使用的一个点就是view模板,使用了SPA之后,每个业务页面不再是独立的html,仅仅是一个segment,所以通常这些segment会 ...

  3. groot 引入外部模板

    index7.html <html><head> <title>groots引入外部模板van</title> <script src=" ...

  4. C++11外部模板

    [C++11之外部模板] 在标准C++中,只要在编译单元内遇到被完整定义的模板,编译器都必须将其实例化(instantiate).这会大大增加编译时间,特别是模板在许多编译单元内使用相同的参数实例化. ...

  5. C++11 : 外部模板(Extern Template)

    在C++98/03语言标准中,对于源代码中出现的每一处模板实例化,编译器都需要去做实例化的工作:而在链接时,链接器还需要移除重复的实例化代码.显然,让编译器每次都去进行重复的实例化工作显然是不必要的, ...

  6. vue组件化之模板优化及注册组件语法糖

    vue组件化之模板优化及注册组件语法糖 vue组件化 模板 优化  在 https://www.cnblogs.com/singledogpro/p/12054895.html 这里我们对vue.js ...

  7. JavaScript模板引擎artTemplate.js——如何引入模板引擎?

    artTeamplate.js在github上的地址:artTemplate性能卓越的js模板引擎 引入模板引擎,就是引入外部javascript啦,并且artTemplate.js不依赖其他第三方库 ...

  8. Win10系列:JavaScript 项目模板中的文件和项模板文件

    通过上面内容的学习,相信读者已经对各种项目模板和项模板有了大致的了解,本节将进一步介绍项目模板中默认包含的项目文件以及项模板文件,首先讲解这些文件中的初始内容以及作用,然后介绍在一个页面中如何添加控件 ...

  9. 根据模板导出Excel报表并复制模板生成多个Sheet页

    因为最近用报表导出比较多,所有就提成了一个工具类,本工具类使用的场景为  根据提供的模板来导出Excel报表 并且可根据提供的模板Sheet页进行复制 从而实现多个Sheet页的需求, 使用本工具类时 ...

随机推荐

  1. 第二回 C#和JAVA 语法差异性对比

    1.继承  C#用 :  java用 extends 继承父类   implements 2.Java : 一个源文件中只能有一个public类  可以有多个非public类  源文件的名称应该和pu ...

  2. 同一个IIS绑定多个Htts 站点问题

    默认情况一个服务器的IIS只能绑定一个HTTPS也就是443端口 要实现多个站点对应HTTPS只能更改IIS配置 地址:C:Windowssystem32inetsrvconfigapplicatio ...

  3. python 9*9 乘法表

    row = 1 while row <= 9: col = 1 while col <= row: print('%d*%d=%d'%(col, row, row*col), end='\ ...

  4. [整理]C语言中字符常量与ASCII码

    所有的ASCII码都可以用“\”加数字(一般是8进制数字)来表示.而C中定义了一些字母前加"\"来表示常见的那些不能显示的ASCII字符,如\0,\t,\n等,就称为转义字符,因为 ...

  5. 爬虫笔记之JS检测浏览器开发者工具是否打开

    在某些情况下我们需要检测当前用户是否打开了浏览器开发者工具,比如前端爬虫检测,如果检测到用户打开了控制台就认为是潜在的爬虫用户,再通过其它策略对其进行处理.本篇文章主要讲述几种前端JS检测开发者工具是 ...

  6. 字典树&&01字典树专题&&对字典树的理解

    对于字典树和01字典树的一点理解: 首先,字典树建树的过程就是按照每个数的前缀来的,如果你要存储一个全小写字母字符串,那么这个树每一个节点最多26个节点,这样的话,如果要找特定的单词的话,按照建树的方 ...

  7. Python标准库笔记(7) — copy模块

    copy-对象拷贝模块:提供了浅拷贝和深拷贝复制对象的功能, 分别对应模块中的两个函数 copy() 和 deepcopy(). 1.浅拷贝(Shallow Copies) copy() 创建的 浅拷 ...

  8. docker stack 部署 rabbitmq 容器

    =============================================== 2018/5/13_第1次修改                       ccb_warlock == ...

  9. Python_oldboy_自动化运维之路_函数,装饰器,模块,包(六)

    本节内容 上节内容回顾(函数) 装饰器 模块 包 1.上节内容回顾(函数) 函数 1.为什么要用函数? 使用函数之模块化程序设计,定义一个函数就相当于定义了一个工具,需要用的话直接拿过来调用.不使用模 ...

  10. ibm x3550m4 开启cpu高性能模式

    1.必须进bios里调整,和调整超线程一样,重启服务器按F1进bios界面,选择system settings 2.选择最大性能模式:Operating Modes>Choose Operati ...