考虑一堆基本类型,Foo
全部具有通用方法的唯一实现Bar()
。我可以将Foo1
,结合起来Foo2
,Foo5
就像这样:
CombinedFoo<Foo1, Foo2, Foo5> combined_foo;
它使用递归继承CombinedFoo
有效地使其与:
class CombinedFoo <Foo1, Foo2, Foo5>
{
Foo1 foo1;
Foo2 foo2;
Foo5 foo5;
public:
void Bar ()
{
foo1.Bar();
foo2.Bar();
foo5.Bar();
}
};
这很方便,但是当我想在运行时选择Foo
将哪些类型组合(合并为单个对象)以发送给函数时,就会遇到问题,例如:
template <typename Foo> void Do (Foo && foo);
使用if
s和switch
s解决3个选项版本的示例解决方案:
int result = 0;
if (criteria_for_foo1)
result += 100;
if (criteria_for_foo2)
result += 10;
if (criteria_for_foo3)
result += 1;
switch (result)
{
case 001 : Do(Foo3());
break;
case 010 : Do(Foo2());
break;
case 011 : Do(CombinedFoo<Foo2, Foo3>());
break;
case 100 : Do(Foo1());
break;
case 101 : Do(CombinedFoo<Foo1, Foo3>());
break;
case 110 : Do(CombinedFoo<Foo1, Foo2>());
break;
case 111 : Do(CombinedFoo<Foo1, Foo2, Foo3>());
break;
default : break;
}
这些if
语句很好,它们呈线性增长,但是switch
随着我们有更多选择,该语句呈指数增长。我的实际问题有4个选项,因此我需要处理16个我不想维护的案例。
我相信没有办法避免可执行文件呈指数增长,但是有没有办法在c ++代码中避免这种情况(在Bar
方法中不引入明显的低效率)?还是有解决此一般问题的已知解决方法/替代方法?
编辑:
为了清楚起见:Do(Foo1); Do(Foo2)
与并不相同Do(CombinedFoo<Foo1, Foo2>())
,将Foo
s组合在一起以实现对的单个调用至关重要Do
。
对于那些想了解现实世界动机的人:这是针对优化问题的,因为我Foo
的问题实际上是可以编辑我的解决方案Generator
的基本问题,Move
然后将其发送到各种启发式方法中。如果我一次只发送Generator
一次,那么我的求解器将重复数千次相同类型的移动,因此总是无能为力/停留在局部最小值(反复考虑相同类型的移动众所周知)影响)。
我在运行时选择其中一些模板参数的原因是因为某些Move
不适用于某些问题实例(我的程序直到运行时才意识到)。
经过很多时间,我终于想到了一个相当简单的通用答案。
#include <type_traits>
// We choose which arguments (Args...)
// to send to the Call method:
template <int N, int N_MAX, typename Caller, typename ... Args>
std::enable_if_t<N == N_MAX>
ChooseTemplateArgumentsRecursive (const bool[])
{
Caller::template Call<Args...>();
}
template <int N, int N_MAX, typename Caller, typename CandidateArg, typename ... Args>
std::enable_if_t<N != N_MAX>
ChooseTemplateArgumentsRecursive (const bool include[])
{
if (include[N])
ChooseTemplateArgumentsRecursive<N+1, N_MAX, Caller, Args..., CandidateArg>(include);
else
ChooseTemplateArgumentsRecursive<N+1, N_MAX, Caller, Args...>(include);
}
// You only need to call this function:
template <typename Caller, typename ... CandidateArgs>
void ChooseTemplateArguments (const bool include[])
{
constexpr int N_MAX = sizeof...(CandidateArgs);
ChooseTemplateArgumentsRecursive<0, N_MAX, Caller, CandidateArgs...>(include);
}
以上适用于c++14
或更高版本。如果您仅有权访问,请c++11
同时更改两个:
std::enable_if_t</*expression*/>
...至:
typename std::enable_if</*expression*/>::type
ChooseTemplateArguments<CallerDo, Foo0, Foo1> (include);
因此,这里我们有一个bool
数组来决定是否对include
参数进行处理。所以,如果include[0]
在true
随后Foo0
将被包括在被发送到参数包Caller::Call
。Call
然后,在站点上可以选择参数包,并可以根据需要进行操作。您可以Call
根据自己的需要设置站点。对于我的示例,它的工作方式如下:
struct CallerDo
{
template <typename ... Foos>
static void Call ()
{
Do(CombinedFoo<Foos...>());
}
};
Call
必须这样命名,并且必须是静态成员。其他一切都可以随心所欲。
参数包保持其原始顺序。
如果Call
站点需要访问任何数据(例如功能输入),则可以始终将其发送给您的Caller
班级/由您的班级管理。
最后,这是一个现场演示。
本文收集自互联网,转载请注明来源。
如有侵权,请联系 [email protected] 删除。
我来说两句