使用模板代替开关

问题描述

| 我想在我的代码中执行一组类似的测试,但仅根据参数进行更改。 我可以使用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语句之外,这样做还有什么好处? 理想情况下,我希望能够编写一种方法来减少维护开销。 谢谢     

解决方法

建立安德鲁的答案... 注意,the2ѭ必须在编译时知道。如果直到运行时才知道它,那么您将不得不编写一个
switch
来选择模板,将您带回到开始的地方。 另一种方法是使用“特征”模式:
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);
}
如果尝试对缺乏特质特性的传感器系列使用“ 5”,则会出现编译错误。还要注意,您从不实例化traits对象,而只是使用其定义。 这样,您就可以在多个函数中重复使用常量,typedef。此外,添加新系列并不涉及梳理所有代码以查找每一个需要的“ 3”语句。您要做的就是创建一个新的
SensorTraits
专业化课程。 编辑:您可以使字段依赖于传感器家族,并带有指向成员的指针:
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;
}
您还可以输入“ 9”来表示传感器的数据类型:
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;
}
我还没有测试这些;您可能需要在其中放一个
const
static
。     ,如果编译器未能正确优化
switch
(即,它生成的代码与模板解决方案的代码不同,这对于现代的内联编译器是可行的),则好处是很小的潜在性能改进。当然,仅当
family
是编译时常量时-否则模板不适用,编译器可以为
switch
找到的最佳优化是计算跳转或跳转表。 如果您的代码始终看起来像“ 16”,我宁愿使用数组作为期望值和传感器ID,并根据“ 14”对它们进行索引。     ,在以下情况下,无法使用模板:
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;
    ,好吧……您正在将处理从运行时转移到编译时。编译器将创建函数并相应地使用它们,而不是在运行时不必通过switch语句。 同样,这将导致更干净的代码。     ,二进制大小也有好处,只有实例化的模板才会在可执行文件中。 如果您有很大的选择,这对exe大小可能很重要!