我要有一个枚举,并且为所有可能的枚举组合定义模板函数,直到长度l。
说枚举是
Enum typenum {A, B, C}
所有这些模板函数都已定义并在运行时可用(即,编译器在编译时创建了这些函数)
Alpha<A>::f()
Alpha<B>::f()
Alpha<C>::f()
Alpha<A,A>::f()
Alpha<A,B>::f()
Alpha<A,C>::f()
Alpha<B,A>::f()
Alpha<B,B>::f()
Alpha<B,C>::f()
Alpha<C,A>::f()
Alpha<C,B>::f()
Alpha<C,C>::f()
and combination of 3 enums, 4 enums...
现在我必须根据输入向量选择合适的功能
void f(vector<enum> eVec){
Alpha::f<eVec[0], eVec[1],... eVec[eVec.size() - 1]>() // <-------
我该怎么做?一种方法是为每种尺寸定义。例如:
if(eVec.size() == 1)
Alpha<eVec[0]>::f()
else if(eVec.size() == 2)
Alpha<eVec[0], eVec[1]>::f()
这不会扩展。是否有任何优雅,可扩展的方式来做到这一点。
答案 0 :(得分:3)
所有这些模板函数都已定义并在运行时可用(即,编译器在编译时创建了这些函数)
您确定这是个好主意吗?
因为,如果要选择运行时模板值,则必须实现编译时所有可能的Alpha<typeNumsValues...>::f()
组合。
如果不对可变参数列表强加一个长度限制,那是不可能的,但是当限制相对较低时,这在计算上也非常昂贵。
无论如何...假设您有一个如下的枚举
enum typeEnum { A, B, C };
和可变参数模板Alpha
类,具有typeEnum
模板值和如下的static
方法f()
template <typeEnum ...>
struct Alpha
{ static void f () { /* do something */ } };
您的f()
可以呼叫可变参数f_helper()
void f (std::vector<typeEnum> const & eVec)
{ f_helper<>(eVec, 0u); }
实现如下
template <typeEnum ...>
void f_helper (std::vector<typeEnum> const &, ...)
{ }
template <typeEnum ... Tes>
typename std::enable_if<(sizeof...(Tes) < 6u)>::type
f_helper (std::vector<typeEnum> const & eVec, std::size_t index)
{
if ( index < eVec.size() )
switch ( eVec[index++] )
{
case A: f_helper<Tes..., A>(eVec, index); break;
case B: f_helper<Tes..., B>(eVec, index); break;
case C: f_helper<Tes..., C>(eVec, index); break;
}
else
Alpha<Tes...>::f();
}
观察到我对可变参数列表的长度设置了非常低的限制(5,sizeof...(Tes) < 6u
),因为已开发的Alpha
的数量呈指数增长。
还要注意,我已经添加了f_helper()
的禁止版本;这是必要的,因为小于6长度的递归调用f_helper()
可以使用必须以某种方式管理的6个枚举的可变列表来调用它。
以下是完整的编译示例
#include <vector>
enum typeEnum { A, B, C };
template <typeEnum ...>
struct Alpha
{ static void f () { } };
template <typeEnum ...>
void f_helper (std::vector<typeEnum> const &, ...)
{ }
template <typeEnum ... Tes>
typename std::enable_if<(sizeof...(Tes) < 6u)>::type
f_helper (std::vector<typeEnum> const & eVec, std::size_t index)
{
if ( index < eVec.size() )
switch ( eVec[index++] )
{
case A: f_helper<Tes..., A>(eVec, index); break;
case B: f_helper<Tes..., B>(eVec, index); break;
case C: f_helper<Tes..., C>(eVec, index); break;
}
else
Alpha<Tes...>::f();
}
void f (std::vector<typeEnum> const & eVec)
{ f_helper<>(eVec, 0u); }
int main ()
{
f({A, B, C, A});
}
答案 1 :(得分:1)
如果要从运行时变量中获取特定功能,请改用map
。模板是工作的错误工具,因为您必须写很多代码才能将变量转换为常量。
假设您enum
的默认值为None
,并且您最多可以说5个参数,则可以这样定义一个映射:
enum MyEnum { None = 0, A, B, C, D... };
using MyKey = std::tuple<MyEnum, MyEnum, MyEnum, MyEnum, MyEnum>;
using MyFunction = std::function<void()>;
然后您在某处有功能图(单例)
std::map<MyKey, MyFunction> myMap;
实用程序功能可能有助于从可变数量的参数创建密钥:
MyKey MakeKey(MyEnum e1, MyEnum e2 = None, MyEnum e3 = None, MyEnum e4 = None, MyEnum e5 = None)
{
return std::make_tuple(e1, e2, e3, e4, e5);
}
myMap.emplace(MakeKey(A, B), [](){ /* some code */ });
MyEnum AtOrDefault(const vector<enum> &eVec, int index)
{
return index < eVec.size() ? eVec[index] : None;
}
然后假设您要从向量中调用适当的函数,则可以执行以下操作:
void f(const vector<enum> &eVec)
{
if (eVec.size() > 5) throw some_exception;
MyKey key = std::make_typle(
AtOrDefault(eVec, 0),
AtOrDefault(eVec, 1),
AtOrDefault(eVec, 2),
AtOrDefault(eVec, 3),
AtOrDefault(eVec, 4));
auto &fn = myMap[key];
fn();
}
您还可以使用假设您知道枚举中元素的最大数量来计算值的想法。然后,您可以创建一个CombinedEnumType:
enum CombinedEnumType : uint32_t { };
并定义了一个函数
CombinedEnumType MakeCombinedEnumType(MyEnum e1, … MyEnum e5 = None)
{
const int MyEnumEcount = 5;
MyEnum a[] = { e1, e2, e3, e4, e5 };
uint32_t result = 0;
for (auto & item : a) { result *= MyEnumEcount; result += item; }
return static_cast<CombinedEnumType>(result);
}
此代码仅供参考。在实际的生产代码中,您必须使用常量,正确的变量名,验证给定组合是否存在函数……
答案 2 :(得分:0)
回答我自己的问题。我已经意识到这种方法行不通。因为当我们写类似
Alpha<eVec[0]>::f()
编译器抛出错误-“错误:表达式必须具有常量值”
所以剩下的唯一选择是
if(eVec.size() == 1){
switch(eVec[0]){
case A:
Alpha<A>::f()
.....
这是因为编译器需要了解所有可能的参数类型,并在编译期间使用哪种模板进行调用。