(C++模板编程):混合元编程(上)

目录

混合元编程

常规的计算点积范例

混合元编程计算点积

C++编译的两个阶段

代码的智能化产生与膨胀

混合元编程

【概念介绍】

  • (a)程序员写出一段元编程代码
  • (b)编译器根据这段代码编译(生成)出来一段新代码,实现程序真正功能的是这段生成的新代码。
  • (c)编译器会对这段新代码进行编译,产生出最终的可执行程序。
  • 混合元编程可以看成是运行期C++代码的生成器。

常规的计算点积范例

  • a)数组a有三个元素,a[0],a[1],a[2],值分别是1,2,3
  • b)数组b有三个元素,b[0],b[1],b[2],值分别是4,5,6
  • c)a和b的点积是一个数值,结果为:a[0]*b[0] + a[1]*b[1]  + a[2]*b[2]  =1*4 + 2*5 + 3*6 = 32
template <typename T,int U> //T:元素类型,U:数组大小
auto DotProduct(T* array1, T *array2)
{
	T dpresult = T{}; //零初始化
	for (int i = 0; i < U; ++i)
	{
		dpresult += array1[i] * array2[i];
	}
	return dpresult;
}
  • 调用
int a[] = { 1,2,3 };
int b[] = { 4,5,6 };
int result = DotProduct<int, 3>(a, b);
cout << result << endl;
  • 查看反汇编代码,调用DotProduct<int,3>实现数组a和数组b的点积运算

【加上inline的情况】

template <typename T,int U> //T:元素类型,U:数组大小
inline auto DotProduct(T* array1, T *array2)
{
	T dpresult = T{}; //零初始化
	for (int i = 0; i < U; ++i)
	{
		dpresult += array1[i] * array2[i];
	}
	return dpresult;
}
  • 调用
int a[] = { 1,2,3 };
int b[] = { 4,5,6 };
int result = DotProduct<int, 3>(a, b);
cout << result << endl;
  • 查看反汇编代码,DotProductinline后产生的一系列汇编代码,进行了函数替换,不是调用函数本体,所以编译器不需要实例化出DotProduct<int,3>

  • 函数调用语句替换成函数本体后,由于模板中存在for循环语句,需要判断是否结束循环条件等等,相应地汇编代码中也会存在判断跳转语句,这会影响程序的执行效率。

混合元编程计算点积

【示例】

//泛化版本
template <typename T,int U>//T:元素类型,U:数组大小
struct DotProduct
{
	static T result(const T* a, const T* b)
	{
		return (*a) * (*b) + DotProduct<T, U - 1>::result(a + 1, b + 1);
	}
};

//特化版本,用于做递归调用的出口
template <typename T>
struct DotProduct<T, 0>
{
	static T result(const T* a, const T* b)
	{
		return T{};
	}
};
  • 调用
int a[] = { 1,2,3 };
int b[] = { 4,5,6 };
int result = DotProduct<int, 3>::result(a, b);
    //编译器经过编译后产生了 (*a) * (*b) + (*(a+1)) * (*(b+1)) + (*(a+2)) * (*(b+2)) 
    //int result =  (*a) * (*b) + (*(a+1)) * (*(b+1)) + (*(a+2)) * (*(b+2)) ;
cout << result << endl;
  • 分析
DotProduct<int,3>被实例化,DotProduct<int,2>被实例化,DotProduct<int,1>被实例化,DotProduct<int, 0>被实例化
DotProduct<int, 3>::result(a, b) = 
(*a) * (*b) + DotProduct<int, 2>::result(a + 1, b + 1) =
(*a) * (*b) + (*(a+1)) * (*(b+1)) + DotProduct<int, 1>::result(a + 2, b + 2) =
(*a) * (*b) + (*(a+1)) * (*(b+1)) + (*(a+2)) * (*(b+2)) +DotProduct<int, 0>::result(a + 3, b + 3) = 
(*a) * (*b) + (*(a+1)) * (*(b+1)) + (*(a+2)) * (*(b+2)) + 0
  • 查看反汇编代码

C++编译的两个阶段

【把整个编译过程分成两个阶段:前期阶段和后期阶段】

  • 前期阶段:此阶段的C++编译器实际充当解释器的角色,直接针对程序员开发的C++源码(元编程代码)进行解释执行,这一阶段的工作成果就是产生了一系列的C++代码(所以元编程才被看做 运行期 C++代码生成器)。
  • 后期阶段:此阶段的C++编译器恢复了C++编译器本应该具备的功能——针对前期阶段产生的结果代码进行编译、链接,最终生成可执行程序。

代码的智能化产生与膨胀

  • 如果针对上面混合编程点积进行如下调用
int a[] = { 1,2,3,4,5,6,7,8,9,10,11,12 };
int b[] = { 4,5,6,7,8,9,1,2,3,10,11,12 };
int result = DotProduct<int, 12>::result(a, b); 
//int result = (*a) * (*b) + (*(a + 1)) * (*(b + 1)) + (*(a + 2)) * (*(b + 2)) + .........+ (*(a + 11)) * (*(b + 11)) 
cout << result << endl;
  • 查看其反汇编代码

  • 通过dumpbin工具查看,编译器实例化相关的函数如下:
DotProduct<int, 0>、DotProduct<int, 1>、DotProduct<int, 2>....DotProduct<int, 12>

DotProduct<int,4>::result ,DotProduct<int,7>::result ,DotProduct<int,10>::result 
  • 结合反汇编代码,发现对函数进行了相关的调用,并不像 U = 3 的情况(生成并执行新的代码,不调用函数)。
  • VS2019编译器在 针对元编程产生一系列C++代码 方面已经具备了相当的智能性——通过一些函数调用避免产生过分冗长的C++代码
  • DotProduct<int, 12>::result(a, b)产生了代码膨胀(最终生成的可执行程序尺寸会变大),编程时应当注意这种情况。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值