如何使用最少的样板实现参数化类模板

时间:2014-02-22 10:42:32

标签: c++ composition template-meta-programming boilerplate

在我的软件中,模板元编程被大量使用,模板类通常将类模板作为参数来定义其行为的某些方面。作为一个非常简单的例子,假设我们有一个类FormulaUser,它需要使用公式来计算两个其他数字的数字,并且必须将特定公式指定为模板参数。此外,关于它操作的数据类型(浮点数或双精度数),公式需要打开。例如:

template <template<typename> class Formula, typename FpType>
struct FormulaUser {
   using TheFormula = Formula<FpType>;

   void some_function ()
   {
       FpType x = 1;
       FpType y = 2;
       FpType result = TheFormula::calculate(x, y);
   }
};

template <typename FpType>
struct AddFormula {
    static FpType calculate (FpType x, FpType y) { return x + y; }
};

// composition:
using TheFormulaUser = FormulaUser<AddFormula, float>;

这没关系,但是当公式本身需要在传递给FormulaUser之前定义参数时,这不是很多。例如,LinearFormula(让我们忽略浮点类型不能作为模板参数的事实):

template <float A, float B, float C>
struct LinearFormula {
    template <typename FpType>
    struct Formula {
        static FpType calculate (FpType x, FpType y) { return A + B*x + C*y; }
    };
};

// composition:
using TheFormulaUser = FormulaUser<LinearFormula<1.0, 2.0, 3.0>::template Formula, float>;

我不喜欢这段代码:

  • 构图很难看(::template Formula part)。
  • LinearFormula的肉缩进了两次。

有没有办法让它更好?

更新

我希望将公式参数分为两个级别(公式常量和FpType)的原因是第一组中的那些是用户配置的一部分,而第二组中的那些是由正在制作的类提供的使用公式。好吧,FpType也最终作为用户配置,但对于所有公式应该是相同的。这个更复杂的构图证明了这一点......

using MyProgram = Program<
    float, // FpType
    AddFormula, // Formula for something
    LinearFormula<2.0, 5.3, 5.3>, // Formula for something else
    QuadraticFormula<.....>, // For something else...
    ExponentialAveraging< // AveragingType
        0.6 // SmoothingFactor
    >
>;

因此,您将FpType提供给根类,并将其传播到其他所有内容。

这些例子是人为的,但它们应该解释这个问题。我不希望配置有任何超出必要的样板(特别是浮动常量不能真正指定如上......)。

最后,要求使用模板元编程(出于性能原因以及与现有代码的一致性)。

1 个答案:

答案 0 :(得分:0)

我意识到你的例子有点人为,但我没有理由看到模板模板参数和嵌套。所以我的建议是删除它们并在公式级别执行所有参数类型参数化。这会创建一个“整洁”和较少嵌套的解决方案,但可能不是对您有用的解决方案。另请注意,我将浮点模板参数移动到策略类中。

template <class Formula>
struct FormulaUser 
{
    using param_type = typename Formula::param_type;

    param_type some_function()
    {
        param_type x = 1;
        param_type y = 2;
        return Formula::calculate(x, y);
    }
};

template <typename FpType>
struct AddFormula 
{
    using param_type = FpType;
    static FpType calculate(FpType x, FpType y) { return x + y; }
};

using TheFormulaUser = FormulaUser<AddFormula<float>>;

struct SomeCoefficients
{
    static float A()
    {
        return 1.0;
    }
    static float B() 
    {
        return 2.0;
    }
    static float C()
    {
        return 3.0;
    }
};

template <typename FPType, typename LinearPolicy>
struct LinearFormula 
{
    using param_type = FPType;
    static FPType calculate(FPType x, FPType y) 
    {
        return LinearPolicy::A() + LinearPolicy::B() * x + LinearPolicy::C() * y; 
    }
};

// composition:
using TheFormulaUser2 = FormulaUser<LinearFormula<float, SomeCoefficients>>;