我的函数 foo 有两个参数 - 一个字符串 str ,一个强类型的枚举 \ n> ENUM1 。它应该实例化一个类模板,其模板参数依赖于str和ENUM1。如何在不使用多个if..then..else语句的情况下实现此目的?
void foo(std::string str, ENUM1 enumVal) {
if (str == "str1" && enumVAL == ENUM1::VAL1)
ExampleClass<STR1TYPE, VAL1TYPE> instance;
else if (str == "str1" && enumVAL == ENUM1::VAL2)
ExampleClass<STR1TYPE, VAL2TYPE> instance;
else if (str == "str2" && enumVAL == ENUM1::VAL1)
ExampleClass<STR2TYPE, VAL1TYPE> instance;
...
}
我无法改变ExampleClass的设计方式,也无法更改进入的参数类型。我讨厌if then else以获取两个参数值的所有排列。
我在考虑保留std::unordered_map<std::pair<std::string, ENUM>, ExampleClassBase>
,但这意味着占用内存以节省代码的可读性。
这样做有什么方法吗?
答案 0 :(得分:1)
嗯,模板和类型特征魔术的一个小例子。
首先 - 单个案例的功能对你很有用:
template <typename StrType, typename EnumType>
void fooSingleCase()
{
std::cout << "In: " << __PRETTY_FUNCTION__ << "\n";
ExampleClass<StrType, EnumType> instance;
// ...
}
如果您需要为不同的StrType / EnumType对提供不同的代码 - 您可以稍微修改一下:
template <typename StrType, typename EnumType>
void fooSingleCaseImplementation(ExampleClass<StrType, EnumType>& instance)
{
// most default implementation
}
void fooSingleCaseImplementation(ExampleClass<STR1TYPE, VAL2TYPE>& instance)
{
// some specific implementation
}
template <typename StrType, typename EnumType>
void fooSingleCase()
{
std::cout << "In: " << __PRETTY_FUNCTION__ << "\n";
ExampleClass<StrType, EnumType> instance;
fooSingleCaseImplementaction(instance);
}
但是如何从foo
转到此fooSingleCase
。首先,您需要使用值连接类型 - 您需要类型特征技术:
template <typename StrType>
struct StrTraits
{
static const std::string pattern;
};
template <typename EnumType>
struct ENUM1Traits
{
static const ENUM1 pattern;
};
特征实例:
template <>
const std::string StrTraits<STR1TYPE>::pattern = "str1";
template <>
const std::string StrTraits<STR2TYPE>::pattern = "str2";
template <>
const ENUM1 ENUM1Traits<VAL1TYPE>::pattern = ENUM1::VAL1;
template <>
const ENUM1 ENUM1Traits<VAL2TYPE>::pattern = ENUM1::VAL2;
template <>
const ENUM1 ENUM1Traits<VAL3TYPE>::pattern = ENUM1::VAL3;
所以 - 我们差不多完成了。我们现在需要制作模板魔术:
首先 - 假设您已经获得了EnumType - 您只需要选择strType - 所以 - 开始使用特征:
template <typename EnumType>
void fooFixedEnumCase(std::string strVal)
{
std::cout << strVal << " not found!\n";
}
template <typename EnumType, typename Str1Type, typename ...StrType>
void fooFixedEnumCase(std::string strVal)
{
if (StrTraits<Str1Type>::pattern == strVal)
fooSingleCase<EnumType, Str1Type>();
else
fooFixedEnumCase<EnumType, StrType...>(strVal);
}
为了选择enumType然后strType - 我们需要两组可变参数模板类型 - 所以让我们使用带有成员函数模板的类模板:
template <typename ...EnumType>
struct Enum1Switch;
template <typename EnumType1, typename ...EnumType>
struct Enum1Switch<EnumType1, EnumType...>
{
template <typename ...StrType>
static void strSwitch(std::string str, ENUM1 enumVal)
{
if (ENUM1Traits<EnumType1>::pattern == enumVal)
fooFixedEnumCase<EnumType1, StrType...>(str);
else
Enum1Switch<EnumType...>::template strSwitch<StrType...>(str, enumVal);
}
};
template <>
struct Enum1Switch<>
{
template <typename ...StrType>
static void strSwitch(std::string str, ENUM1 enumVal)
{
std::cout << static_cast<int>(enumVal) << " not found!\n";
}
};
然后你的foo将如此简单:
void foo(std::string str, ENUM1 enumVal) {
Enum1Switch<VAL1TYPE, VAL2TYPE, VAL3TYPE>
::strSwitch<STR1TYPE, STR2TYPE>(str, enumVal);
}
中的示例
问题 - 如何使用新类型扩展此机制。喜欢STRTYPENEW,VALTYPENEW。只需要几步:
新特征:
template <>
const std::string StrTraits<STRTYPENEW>::pattern = "newstr";
template <>
const ENUM1 ENUM1Traits<VALTYPENEW>::pattern = ENUM1::NEW_VAL;
foo只是一个小小的变化:
void foo(std::string str, ENUM1 enumVal) {
Enum1Switch<VAL1TYPE, VAL2TYPE, VAL3TYPE, VALTYPENEW>
// ^^^^^^^^^^^^
::strSwitch<STR1TYPE, STR2TYPE, STRTYPENEW>(str, enumVal);
// ^^^^^^^^^^^^
}
最后一条评论 - 如果你把所有这些函数都内联起来 - 为这个模板魔法生成的代码(我的意思是汇编程序代码)和你的初始if / else方法是非常相同的。