【编程技术】C++ CRTP & Expression Templates

目录

概要

奇异递归模板模式(CRTP)

解决的问题与实现原理

 表达式模板(Expression Templates)

解决的问题与实现原理

示例代码

示例程序对应的CRTP和编译树


概要

最近在学习Eigen 线性代数计算库的代码,对其中很多C++的模板定义不甚理解,作为一名资深码农甚是惭愧。对于解决一个向量加法表达式v3 = v0 + v1 + v2,传统的编程方式存在中间内存的申请与释放、多次循环遍历等空间和时间的低效问题,不符合Eigen的高性能计算需求,为了提升性能,Eigen用了很多模板元编程技术,本文总结了其中两个知识点

  • 奇异递归模板模式(CRTP)
  • 表达式模板(Expression Templates)

 

奇异递归模板模式(CRTP)

动机与原理

  • 用父类提供统一的API 接口
  • 在Compile编译阶段确定调用子类接口实现的静态多态能力(相对于运行线虚函数的动态多态提升了运行时性能)
  • 实现原理如下图所示

 表达式模板(Expression Templates)

动机与原理

  • 延迟计算表达式,从而可以将表达式传递给函数参数,而不是只能传表达式的计算结果
  • 节省表达式中间计算结果的临时存储空间,减少向量等线性代数计算的循环次数,从而减少整体计算的空间和时间成本
  • 实现原理如下图所示

示例代码

上面的原理图看起来还是比较难以理解的,下面以求解一个向量加法赋值表达式:v3 = v0 + v1 + v2 的程序代码为例解析原理图中的涵义

#include <cassert>
#include <iostream>
#include <vector>

//CRTP中的基类模板
template <typename E>
class VecExpression {
public:
    //通过将自己static_cast成为子类,调用子类的对应函数实现实现静态多态
    double operator[](size_t i) const { return static_cast<E const&>(*this)[i];     }
    size_t size()               const { return static_cast<E const&>(*this).size(); }

};

 

//将自己作为基类模板参数的子类 - 对应表达式编译树中的叶节点
class Vec : public VecExpression<Vec> {
    std::vector<double> elems;

  public:
    double operator[](size_t i) const { return elems[i]; }
    double &operator[](size_t i)      { return elems[i]; }
    size_t size() const               { return elems.size(); }

    Vec(size_t n) : elems(n) {}

    Vec(std::initializer_list<double>init){
        for(auto i:init)
            elems.push_back(i);
    }


    //赋值构造函数可以接受任意父类VecExpression的实例,并且进行表达式的展开(对应表达式编译树中的赋值运算符节点)
    template <typename E>
    Vec(VecExpression<E> const& vec) : elems(vec.size()) {
        for (size_t i = 0; i != vec.size(); ++i) {
            elems[i] = vec[i];
        }
    }
};
//将自己作为基类模板参数的子类 - 对应表达式编译树中的二元运算符输出的内部节点
//该结构的巧妙之处在于模板参数E1 E2可以是VecSum,从而形成VecSum<VecSum<VecSum ... > > >的嵌套结构,体现了表达式模板的精髓:将表达式计算改造成为了构造嵌套结构
template <typename E1, typename E2>
class VecSum : public VecExpression<VecSum<E1, E2> > {
    E1 const& _u;
    E2 const& _v;
public:
    VecSum(E1 const& u, E2 const& v) : _u(u), _v(v) {
        assert(u.size() == v.size());
    }

    double operator[](size_t i) const { return _u[i] + _v[i]; }
    size_t size()               const { return _v.size(); }
};
//对应编译树上的二元运算符,将加法表达式构造为VecSum<VecSum... > >的嵌套结构
template <typename E1, typename E2>
VecSum<E1,E2> const operator+(E1 const& u, E2 const& v) {
   return VecSum<E1, E2>(u, v);
}
//主函数入口
int main() {
 //创建3个叶子节点
 Vec v0 = {1.0, 1.0, 1.0};
 Vec v1 = {2.0, 2.0, 2.0};
 Vec v2 = {3.0, 3.0, 3.0};

 //构建表达式 v0 + v1 + v2,赋值给v3,编译阶段形成表达式树
 Vec v3 = v0 + v1 + v2;

//输出结算结果
 for (int i = 0; i < v3.size(); i ++) {
    std::cout <<" "<< v3[i];
 }
  std::cout << std::endl;
}
编译程序:
g++ -I /usr/local/include/eigen3/ -Wall -std=c++11 -fpermissive -g vecAdd.cpp -o vecAdd

运行程序
./vecAdd

程序输出:
 6 6 6

 

示例程序对应的CRTP和编译树

示例程序对应的CRTP 继承关系

 

示例程序对应的表达式编译树

 

 

  • 5
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
写一段JQuery 实现以下逻辑 var hd =$('#txtHeadRailQty').val();// 这个是Common ValanceHeadRail Number: if(hd == 2) //HeadRail Number:2 var cm =$('#CommonValance').val(); //这个是Blind Type if(cm == 'Common Valance') //Blind Type為Common Valance var mt= $('#txtMeasuringType').val()// 这个为WS 或者MFF if(mt =='WS') 这个为WS if(mt == 'MFF') 这个为MFF var lw =$('#txtLeftWidth').val();//这个是Left Width var ltp =$('#txtLeftTiltPos').val(); 这个是Left Width 对应的wand var cw =$('#txtCenterWidth').val();这个 是Center Left Width var ltp =$('#txtCenterTiltPos').val(); 这个是Center Left Width 对应的wand var cwb =$('#txtCenterWidthB').val();//这个是Center Right Width var ltp =$('#txtCenterTiltPosB').val(); 这个是Center Right Width 对应的wand var rw =$('#txtRightWidth').val();//这个是Right Width var ltp =$('#txtRightTiltPos').val(); 这个是Right Width 对应的wand var lgw = $('#txtLeftGapWidth').val() // 这个是Left Gap Width var rgw =$('#txtRightGapWidth').val() //这个是Right Gap Width var cgw = $('#txtCenterGapWidth').val() // 这个是Center Gap Width HeadRail Number:2,WS時 Left Width在 165mm+5mm+Left Gap Width/2~380+5mm+Left Gap Width/2時,Wand值需顯示C Right Width在 165mm+5mm+Left Gap Width/2~380+5mm+Left Gap Width/2時,Wand值需顯示C HeadRail Number:3,WS時 Left Width在 165mm+5mm+Left Gap Width/2~380+5mm+Left Gap Width/2時,Wand值需顯示C Center Width 在 165mm+Left Gap Width/2+Right Gap Width/2~380+Left Gap Width/2+Right Gap Width/2 時,Wand值需顯示C Right Width在 165mm+5mm+ Right Gap Width/2~380+5mm+ Right Gap Width/2時,Wand值需顯示C HeadRail Number:4,WS時 Left Width在 165mm+5mm+Left Gap Width/2~380+5mm+Left Gap Width/2時,Wand值需顯示C Center Left Width 在 165mm+Left Gap Width/2+ Center Gap Width/2~380+Left Gap Width/2+ Center Gap Width/2時,Wand值需顯示C Center Right Width 在 165mm+ Center Gap Width/2+ Right Gap Width/2~380+ Center Gap Width/2+ Right Gap Width/2時,Wand值需顯示C Right Width在 165mm+5mm+ Right Gap Width/2~380+5mm+ Right Gap Width/2時,Wand值需顯示C HeadRail Number:2,MFF時 Left Width在 165mm+Left Gap Width/2~380+Left Gap Width/2時,Wand值需顯示C Right Width在 165mm+Left Gap Width/2~380+Left Gap Width/2時,Wand值需顯示C HeadRail Number:3,MFF時 Left Width在 165mm+Left Gap Width/2~380+Left Gap Width/2時,Wand值需顯示C Center Width 在 165mm+Left Gap Width/2+Right Gap Width/2~380+Left Gap Width/2+Right Gap Width/2 時,Wand值需顯示C Right Width在 165mm+ Right Gap Width/2~380+ Right Gap Width/2時,Wand值需顯示C HeadRail Number:4,MFF時 Left Width在 165mm+Left Gap Width/2~380+Left Gap Width/2時,Wand值需顯示C Center Left Width 在 165mm+Left Gap Width/2+ Center Gap Width/2~380+Left Gap Width/2+ Center Gap Width/2時,Wand值需顯示C Center Right Width 在 165mm+ Center Gap Width/2+ Right Gap Width/2~380+ Center Gap Width/2+ Right Gap Width/2時,Wand值需顯示C Right Width在 165mm+ Right Gap Width/2~380+ Right Gap Width/2時,Wand值需顯示C
06-13
以下是使用JQuery实现以上逻辑的代码: var hd = $('#txtHeadRailQty').val(); var cm = $('#CommonValance').val(); var mt = $('#txtMeasuringType').val(); var lw = $('#txtLeftWidth').val(); var ltp = $('#txtLeftTiltPos').val(); var cw = $('#txtCenterWidth').val(); var cltp = $('#txtCenterTiltPos').val(); var cwb = $('#txtCenterWidthB').val(); var crtp = $('#txtCenterTiltPosB').val(); var rw = $('#txtRightWidth').val(); var rtp = $('#txtRightTiltPos').val(); var lgw = $('#txtLeftGapWidth').val(); var rgw = $('#txtRightGapWidth').val(); var cgw = $('#txtCenterGapWidth').val(); if (hd == 2) { if (mt == 'WS') { if (lw >= 165 + 5 + lgw / 2 && lw <= 380 + 5 + lgw / 2) { // display Wand value for Center } if (rw >= 165 + 5 + rgw / 2 && rw <= 380 + 5 + rgw / 2) { // display Wand value for Center } } } else if (hd == 3) { if (mt == 'WS') { if (lw >= 165 + 5 + lgw / 2 && lw <= 380 + 5 + lgw / 2) { // display Wand value for Center } if (cw >= 165 + lgw / 2 + rgw / 2 && cw <= 380 + lgw / 2 + rgw / 2) { // display Wand value for Center } if (rw >= 165 + 5 + rgw / 2 && rw <= 380 + 5 + rgw / 2) { // display Wand value for Center } } } else if (hd == 4) { if (mt == 'WS') { if (lw >= 165 + 5 + lgw / 2 && lw <= 380 + 5 + lgw / 2) { // display Wand value for Center } if (cw >= 165 + lgw / 2 + cgw / 2 && cw <= 380 + lgw / 2 + cgw / 2) { // display Wand value for Center } if (cwb >= 165 + cgw / 2 + rgw / 2 && cwb <= 380 + cgw / 2 + rgw / 2) { // display Wand value for Center } if (rw >= 165 + 5 + rgw / 2 && rw <= 380 + 5 + rgw / 2) { // display Wand value for Center } } } else if (hd == 2) { if (mt == 'MFF') { if (lw >= 165 + lgw / 2 && lw <= 380 + lgw / 2) { // display Wand value for Center } if (rw >= 165 + lgw / 2 && rw <= 380 + lgw / 2) { // display Wand value for Center } } } else if (hd == 3) { if (mt == 'MFF') { if (lw >= 165 + lgw / 2 && lw <= 380 + lgw / 2) { // display Wand value for Center } if (cw >= 165 + lgw / 2 + rgw / 2 && cw <= 380 + lgw / 2 + rgw / 2) { // display Wand value for Center } if (rw >= 165 + rgw / 2 && rw <= 380 + rgw / 2) { // display Wand value for Center } } } else if (hd == 4) { if (mt == 'MFF') { if (lw >= 165 + lgw / 2 && lw <= 380 + lgw / 2) { // display Wand value for Center } if (cw >= 165 + lgw / 2 + cgw / 2 && cw <= 380 + lgw / 2 + cgw / 2) { // display Wand value for Center } if (cwb >= 165 + cgw / 2 + rgw / 2 && cwb <= 380 + cgw / 2 + rgw / 2) { // display Wand value for Center } if (rw >= 165 + rgw / 2 && rw <= 380 + rgw / 2) { // display Wand value for Center } } }

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值