我们都知道模板是泛型的,但是,它一旦被实例化就会产生一个实例化的副本。
好了,大家应该能够猜到,低效模板和高效模板的差异了
一般的低效模板:
1.泛型实参表达形式多样导致的低效模板
2.多文件引用同一个模板形成的低效模板
我们接下来就分别来解决上述两种模板的低效问题
泛型实参表达形式多样导致的低效模板
模板头是使得泛型具体化的外交大使(泛型形式参数),但是,会出现这种现象:“相同的实例化类型,但是实参表达形式不同,而导致模板头每次都会为相同的泛型参数实例化一个副本,使得同时存在很多一模一样的实例化模板”。而我们正常情况下,一个实例化类型仅仅对应一个实例化副本即可。
比如说:
template<class T> void f(const T& t) { static int time = 0; cout << t << endl; time++; cout << "current time = " << time << " &time = " << &time << endl; cout << endl; } int main() { f(2); f(3); f(3.5); f(8); }
我们模板的实参自动推导为 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{ 0 }; 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; } };
1 #include "test.h" 2 3 double couple(double x) { return 2.0*x; } 4 double square(double x) { return x*x; } 5 6 int main() 7 { 8 double t = 3.5; 9 cout << "use Function pointer couple:" << endl 10 << " " << fun<double>(t, couple) << endl; 11 cout << "use Function pointer square:" << endl 12 << " " << fun<double>(t, square) << endl; 13 14 cout << "use Function object Fq:" << endl 15 << " " << fun<double>(t, Fq()) << endl; 16 cout << "use Function object Fp:" << endl 17 << " " << fun<double>(t, Fp()) << endl; 18 19 cout << "use Lambda expression 1:" << endl 20 << " " << fun<double>(t, [](double u) {return 2 * u; }) << endl; 21 cout << "use Lambda expression 2:" << endl 22 << " " << fun<double>(t, [](double u) {return u*u; }) << endl; 23 24 }
我们可以很明显地看到,主函数中两两一组,一共有三组
第一组中的第二个函数实参为一个函数指针
第二组中的第二个函数实参为一个函数对象
第三组中的第二个函数实参为一个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 2 * 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{ 0 }; 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{ 0 }; count++; cout << "count = " << count << ", &count = " << &count << endl; cout << "value : " << x << endl << endl; } void test1();
#include "test.h" void test1() { opera(3.14); }
#pragma once class test2 { public: test2(){} void solve(double); };
#include "test2.h" #include "test.h" void test2::solve(double x) { opera(x); }
#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); }
上述描述中提到,创建两个cpp然后引用模板头文件,我创建了三个cpp,分别引用模板头文件,然后进行同类型泛型参数测试,结果:所有的模板实例化副本只有一个:
如有高见,请于下方留言,谢谢