使用模板而不是开关

Dan*_*anS 8 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;
  }
}
Run Code Online (Sandbox Code Playgroud)

我想用模板特化来做这件事

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);
}
Run Code Online (Sandbox Code Playgroud)

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

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

谢谢

Mik*_*one 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);
}
Run Code Online (Sandbox Code Playgroud)

如果您尝试使用doTest缺少特征特化的传感器系列,则会出现编译错误.另请注意,您永远不会实例化traits对象,只需使用其定义即可.

这使您可以在多个函数中重用常量,typedef等.此外,添加新系列不涉及梳理所有代码以查找关注的每个switch语句.您所要做的就是创建一个新的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;
}
Run Code Online (Sandbox Code Playgroud)

您也可以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;
}
Run Code Online (Sandbox Code Playgroud)

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


Ale*_*ler 6

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

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