使用模板而不是开关

时间:2011-04-13 13:37:44

标签: c++ templates

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

我可以使用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语句之外,这样做有什么好处吗?

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

感谢

5 个答案:

答案 0 :(得分:8)

建立安德鲁的答案......

请注意,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与缺少特征特化的传感器系列一起使用,则会出现编译错误。另请注意,您永远不会实例化traits对象,只需使用其定义。

这使您可以在多个函数中重用常量,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

答案 1 :(得分:6)

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

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

答案 2 :(得分:2)

在以下情况下不可能使用模板:

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;

答案 3 :(得分:1)

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

此外,它将使代码更清晰。

答案 4 :(得分:0)

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