【问题标题】:Using template instead of switch使用模板代替开关
【发布时间】:2011-08-04 17:53:43
【问题描述】:

我想在我的代码中执行一组类似但仅根据参数更改的测试。

我可以用 switch 语句来写这个:

bool doTest(EnumSensorFamily family, const StructSensorProposal& proposed)
{
  switch (family)
  {
  case FAM1:
    return (ExpectedFam1 == proposed.Fam1SensorId);
    break;
  case FAM2:
    return (ExpectedFam2 == proposed.Fam2SensorId);
    break;
  case FAM3:
    return (ExpectedFam3 == proposed.Fam3SensorId);
    break;
  default:
    ERROR ("Unexpected family");
    return false;
  }
}

我正在考虑使用模板专业化来做这件事

template <EnumSensorFamily family>
bool doTest(const StructSensorProposal& proposed);

template<>
bool doTest<FAM1> (const StructSensorProposal& proposed)
{
  return (ExpectedFam1 == proposed.Fam1SensorId);
}

template<>
bool doTest<FAM2> (const StructSensorProposal& proposed)
{
  return (ExpectedFam2 == proposed.Fam2SensorId);
}

template<>
bool doTest<FAM3> (const StructSensorProposal& proposed)
{
  return (ExpectedFam3 == proposed.Fam3SensorId);
}

除了避免包含几乎相同案例的 switch 语句之外,这样做还有什么好处吗?

理想情况下,我希望能够编写单一方法来减少维护开销。

谢谢

【问题讨论】:

    标签: c++ templates


    【解决方案1】:

    以安德鲁的回答为基础......

    请注意,EnumSensorFamily family 必须在编译时已知。如果直到运行时才知道,那么您必须写一个switch 来选择模板,让您回到开始的地方。

    另一种方法是使用 Traits 模式:

    template <EnumSensorFamily family>
    struct SensorTraits;
    
    template <>
    struct SensorTraits<FAM1>
    {
        const EnumSensorFamily kFamilyID = ExpectedFam1;
    };
    
    template <>
    struct SensorTraits<FAM2>
    {
        const EnumSensorFamily kFamilyID = ExpectedFam2;
    };
    
    template <>
    struct SensorTraits<FAM3>
    {
        const EnumSensorFamily kFamilyID = ExpectedFam3;
    };
    
    template <EnumSensorFamily family>
    bool doTest(const StructSensorProposal& proposed)
    {
      return (SensorTraits<family>::kFamilyID == proposed.Fam1SensorId);
    }
    

    如果您尝试将 doTest 与缺少特征特化的传感器系列一起使用,则会出现编译错误。另请注意,您永远不会实例化特征对象,您只需使用它的定义。

    这使您可以在多个函数中重用常量、typedef 等。此外,添加一个新系列并不涉及梳理所有代码以查找每个关心的switch 语句。您所要做的就是创建一个新的SensorTraits 专业化。

    编辑:您可以使用 pointer to member 使字段依赖于传感器系列:

    template <>
    struct SensorTraits<FAM1>
    {
        const EnumSensorFamily kFamilyID = ExpectedFam1;
        int StructSensorProposal::*proposalField = &StructSensorProposal::fam1field;
    };
    
    // ...
    
    template <EnumSensorFamily family>
    int getProposedField(const StructSensorProposal& proposed)
    {
        return proposed.*SensorTraits<family>::proposalField;
    }
    

    您也可以为传感器的数据类型输入 typedef

    template <>
    struct SensorTraits<FAM1>
    {
        const EnumSensorFamily kFamilyID = ExpectedFam1;
        typedef uint16_t data_type;
        data_type StructSensorProposal::*proposalField = &StructSensorProposal::fam1field;
    };
    
    // ...
    
    template <EnumSensorFamily family>
    SensorTraits<family>::data_type getProposedField(const StructSensorProposal& proposed)
    {
        return proposed.*SensorTraits<family>::proposalField;
    }
    

    我没有测试过这些;你可能需要一个conststatic

    【讨论】:

    • 我可以使用类似的机制来选择建议结构中的字段吗?每个传感器系列在提案中都有一个单独的字段。
    • 我在特征中放置了一个静态方法,以根据系列返回校正后的传感器。
    • 我在特征实现中有一个方法:template &lt;&gt; struct SensorTraits&lt;FAM1&gt; { static EnumSensorId getProposed(const StructSensorProposal&amp; proposed) {return proposed.Fam1SensorId;} }; 它似乎确实有效。这有什么缺点吗?
    • 这是一个完全可行的替代方案。对其他人来说可能不太清楚(getter 函数可能有副作用,而成员取消引用非常明确),但如果读者不理解成员指针,则相反。
    【解决方案2】:

    如果编译器未能正确优化switch(即,如果它没有生成与模板解决方案相同的代码,这对于现代的内联编译器是可行的),好处是一个非常小的潜在性能改进. 当然只有当family 是编译时常量时 - 否则模板不适用,编译器可以为switch 找到的最佳优化将是计算跳转或跳转表。

    如果您的代码总是看起来像 return (ExpectedFamN == proposed.FamNSensorId);,我宁愿使用数组作为预期值和传感器 ID,并根据 family 对它们进行索引。

    【讨论】:

    • 我使用了这个建议来设置一个期望值的数组,但是按照 Mike 的建议来确定传感器字段。谢谢
    【解决方案3】:

    以下情况无法使用模板:

    const  EnumSensorFamily familyCompileTime = FAM3; // Compile time constant
    EnumSensorFamily family = GetFimilyInRunTime(); // Run time variable
    doTest1(family, proposed); // ok
    doTest2<family>(proposed); // error;
    doTest2<familyCompileTime >(proposed); // OK;
    

    【讨论】:

    • 是的,它在编译时是已知的。感谢您澄清问题。
    【解决方案4】:

    在某种程度上...您正在将处理从运行时转移到编译时。编译器将创建函数并相应地使用它们,而不是在运行时必须通过 switch 语句。

    此外,它会产生更清晰的代码。

    【讨论】:

    • 更干净的代码绝对是主观的事情。我个人会发现switch 更干净,因为我不会一直创建和阅读模板源代码。 (不计算 STL 的使用量。)
    • @RedX:另一方面,正如我所指出的,添加传感器系列意味着找到每个 switch 并添加所需的 case
    【解决方案5】:

    二进制大小也有好处,只有实例化的模板才会在可执行文件中。 如果你有一个巨大的开关,它可能对 exe 的大小很重要!

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2014-03-31
      • 1970-01-01
      • 2016-07-24
      • 1970-01-01
      • 1970-01-01
      • 2023-03-07
      相关资源
      最近更新 更多