You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何采用CRTP模式重写含虚函数的C++处理代码?

问题描述

原本的静态硬编码测试代码改成动态绑定实现后,发现虚调用的速度慢了约一倍。原代码如下:

#include <vector>

class vUnit {
  public:
    virtual ~vUnit() {;}
    virtual float process(float f) = 0;
};

class vRack {
  public:
    vRack() {;}
    ~vRack() {;}
    
    inline void addUnit(vUnit* unit_ptr) {
      _units.push_back(unit_ptr);
    }
    
    inline float process_all(float  f) { 
      float  res = 0.;
      for (int j = 0 ; j< 60; j++) {
        for (auto u : _units) {
          f = u->process(f);
        }
      }
      res += f;
      return res;
    }

  private:
    std::vector<vUnit*>  _units ; 
    int _i = 0;
};


class vMultiplyF : public vUnit {
  public:
    vMultiplyF(float v) {_f = v;}
    inline float process(float f) final {
      return f * _f;
    }
  private:
    float  _f;
};

class vDivideF : public vUnit {
  public:
    vDivideF(float v) {_f = v;}
    inline float process(float f) final {
      if (f > 0) {
        return _f / f;
      } else return 0.0f;
    }
  private:
    float  _f;
};

int main () {
  float f = 1.5f, r = 0.0f;
  vRack       vR;
  vMultiplyF  vM(5.1f);
  vDivideF    vD(5.5f);

  vR.addUnit(&vM);
  vR.addUnit(&vD);

  for( int i = 0 ; i < 1000 ; i++) {
    f = vR.process_all(f);
    r += f;
  }
  return 0;
}

我认为CRTP是合理的折中方案,但不清楚如何改写vRack.addUnit()方法,使其能够接受不同的vUnit实现。


基于CRTP的解决方案

CRTP(奇异递归模板模式)可通过静态多态消除虚函数的运行时开销,同时结合类型擦除让vRack支持存储不同类型的CRTP派生类,具体改写步骤如下:

1. 定义CRTP基类

替换原有的虚基类vUnit,实现静态多态的基础框架:

template <typename Derived>
class UnitCRTP {
public:
    float process(float f) {
        // 静态绑定调用派生类的具体实现,无虚函数开销
        return static_cast<Derived*>(this)->process_impl(f);
    }
};

2. 改写原有Unit类

让vMultiplyF和vDivideF继承UnitCRTP,并实现process_impl方法(无需再写虚函数):

class vMultiplyF : public UnitCRTP<vMultiplyF> {
public:
    vMultiplyF(float v) : _f(v) {}
    float process_impl(float f) {
        return f * _f;
    }
private:
    float _f;
};

class vDivideF : public UnitCRTP<vDivideF> {
public:
    vDivideF(float v) : _f(v) {}
    float process_impl(float f) {
        if (f > 0) {
            return _f / f;
        } else return 0.0f;
    }
private:
    float _f;
};

3. 改造vRack实现类型擦除

通过std::function实现类型擦除,让vRack能存储任意符合签名的可调用对象:

#include <vector>
#include <functional>

class vRack {
public:
    vRack() = default;
    ~vRack() = default;

    // 模板化addUnit,接受任意CRTP派生类实例
    template <typename UnitType>
    void addUnit(UnitType& unit) {
        // 将unit的process方法包装为统一签名的函数对象
        _units.emplace_back([&unit](float f) {
            return unit.process(f);
        });
    }

    float process_all(float f) {
        float res = 0.;
        for (int j = 0; j < 60; j++) {
            for (auto& func : _units) {
                f = func(f);
            }
        }
        res += f;
        return res;
    }

private:
    // 存储统一签名的函数对象,替代原有的基类指针容器
    std::vector<std::function<float(float)>> _units;
};

4. 主函数直接复用

原main函数无需修改即可正常运行,addUnit现在能自动适配不同类型的CRTP派生类:

int main () {
    float f = 1.5f, r = 0.0f;
    vRack       vR;
    vMultiplyF  vM(5.1f);
    vDivideF    vD(5.5f);

    vR.addUnit(vM);
    vR.addUnit(vD);

    for( int i = 0 ; i < 1000 ; i++) {
        f = vR.process_all(f);
        r += f;
    }
    return 0;
}

方案优势

  • CRTP通过静态绑定让编译器可对process_impl做内联优化,性能接近静态硬编码;
  • std::function的类型擦除保留了动态添加不同类型Unit的灵活性;
  • 完全规避了虚函数表查找的运行时开销。

内容的提问来源于stack exchange,提问作者Copych

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.17 16:04:49