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

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

一般的低效模板:

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. JS中的作用域和闭包

    作用域:在编程语言中,作用域控制着变量与参数的可见性及生命周期.JS确实有函数作用域,那意味着定义在函数中的参数和变量在函数外部是不可见的,而且在一个函数中的任何位置定义的变量在该函数中的任何地方都是 ...

  2. C++中全排列函数next_permutation用法

    最近做了TjuOj上关于全排列的几个题,室友告诉了一个非常好用的函数,谷歌之,整理如下: next_permutation函数 组合数学中经常用到排列,这里介绍一个计算序列全排列的函数:next_pe ...

  3. 【译】第二篇 Integration Services:SSIS数据泵

    本篇文章是Integration Services系列的第二篇,详细内容请参考原文. 简介SSIS用于移动数据.数据流任务提供此功能.因为这个原因,当介绍SSIS时我喜欢从数据流任务开始.数据流任务的 ...

  4. 二次开发中cad字体的总结

    目前手头一个项目,关于制图统一平台的,特别研究了CAD中的字体,总结出来,给需要的朋友,希望少走弯路.1.cad2008中,netload之后,输入注册的命令,提示未知命令解决:将引用中CAD两个dl ...

  5. JS设计模式——10.门面模式

    门面模式 这是一种组织性的模式,它可以用来修改类和对象的接口,使其更便于使用.它可以让程序员过得更轻松,使他们的代码变得更容易管理. 门面模式有两个作用: 简化类的接口 消除与使用她的客户代码之间的耦 ...

  6. 容斥原理&&莫比乌斯专题

    A题:A - Eddy's爱好   HDU - 2204 具体思路:如果是求n中,为平方数的有多少个,那么答案肯定是sqrt(n),同理,如果是三次根号的话,那么答案肯定是n的三分之一次方.然后继续按 ...

  7. weblogica domain目录 环境变量 如何启动weblogic server

    手工启动weblogic server

  8. linux下的usb抓包方法【转】

    转自:http://blog.chinaunix.net/uid-11848011-id-4508834.html 1.配置内核使能usb monitor: make menuconfig       ...

  9. 你的组织使用了 windows defender 应用程序控制来阻止此应用

    =============================================== 2018/2/8_第1次修改                       ccb_warlock === ...

  10. 利用mysql的binlog恢复数据

    MySQL Binary Log也就是常说的bin-log, ,是mysql执行改动产生的二进制日志文件,其主要作用有两个: * 数据回复 * 主从数据库.用于slave端执行增删改,保持与maste ...