我想在我的代码中执行一组类似的测试,但仅根据参数进行更改。
我可以使用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语句之外,这样做有什么好处吗?
理想情况下,我希望能够编写单一方法来减少维护开销。
感谢
答案 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;
}
我没有测试过这些;您可能需要const
或static
。
答案 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大小上很重要!