【问题标题】:How to implement parametrized class templates with minimal boilerplate如何用最少的样板实现参数化的类模板
【发布时间】:2014-02-22 10:42:32
【问题描述】:

在我经常使用模板元编程的软件中,模板类通常将类模板作为参数来定义其行为的某些方面。作为一个非常简单的例子,假设我们有一个类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 提供给根类,然后将其传播到其他所有内容。

这些例子是人为的,但它们应该能解释问题。我不希望配置有比必要更多的样板文件(特别是,不能像上面那样真正指定浮点常量......)。

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

【问题讨论】:

  • 双缩进和::template 是嵌套的结果,但是您没有激发嵌套,甚至没有提供有效的说明。解决方案显然是避免嵌套,但鉴于此信息,我们无法帮助解决该问题。
  • @Potatoswatter 查看更新
  • 您似乎处于表达式模板所解决的解决方案空间中。您可能会研究这些是如何完成的。组合是通过函数重载,而不是嵌套的模板参数列表。无论如何,这只是我的风格,但不管 TMP 的复杂性如何,我个人都避免将模板模板参数暴露给用户。几乎总是有更好的选择。

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


【解决方案1】:

我意识到您的示例有些做作,但我看不出模板模板参数和嵌套的原因。所以我的建议是删除它们并在公式级别进行所有参数类型参数化。这会创建一个“更整洁”且嵌套更少的解决方案,但可能对您没有用处。另请注意,我将浮点模板参数移到了策略类中。

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>>;

【讨论】:

  • 这对我没用,因为我需要能够在 FormulaUser 中指定一次 FpType。 FormulaUser 可能采用许多对 FpType 开放的此类。
  • 为了清楚起见,我建议从using TheFormulaUser = FormulaUser&lt;AddFormula, float&gt;; 的语法转移到using TheFormulaUser = FormulaUser&lt;AddFormula&lt;float&gt;&gt;;。从实例化的角度来看,它没有太多内容。无论如何,如果这对我没有帮助的话。
  • FormulaUser 组合是在一个文件中完成的,该文件是一种用户配置,并且在其中包含所有这些浮点参数并不好(对于许多公式选择)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2011-05-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-26
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多