是否可以在运行时决定调用哪个模板函数?就像是:
template<int I>
struct A {
static void foo() {/*...*/}
};
void bar(int i) {
A<i>::f(); // <-- ???
}
处理模板时连接编译时间和运行时的典型“技巧”是访问变体类型。例如,这就是通用图像库(可作为 Boost.GIL 或独立的)所做的。它通常采用以下形式:
typedef boost::variant<T, U, V> variant_type;
variant_type variant = /* type is picked at runtime */
boost::apply_visitor(visitor(), variant);
wherevisitor
是一个简单地转发到模板的多态函子:
struct visitor: boost::static_visitor<> {
template<typename T>
void
operator()(T const& t) const
{ foo(t); } // the real work is in template<typename T> void foo(T const&);
};
这有一个很好的设计,模板将/可以实例化的类型列表(这里是variant_type
类型同义词)不与代码的其余部分耦合。类似的元函数boost::make_variant_over
还允许对要使用的类型列表进行计算。
由于此技术不适用于非类型参数,因此您需要手动“展开”访问,不幸的是,这意味着代码不具有可读性/可维护性。
void
bar(int i) {
switch(i) {
case 0: A<0>::f(); break;
case 1: A<1>::f(); break;
case 2: A<2>::f(); break;
default:
// handle
}
}
处理上述开关中的重复的常用方法是(ab)使用预处理器。使用 Boost.Preprocessor 的(未经测试的)示例:
#ifndef LIMIT
#define LIMIT 20 // 'reasonable' default if nothing is supplied at build time
#endif
#define PASTE(rep, n, _) case n: A< n >::f(); break;
void
bar(int i) {
switch(i) {
BOOST_PP_REPEAT(LIMIT, PASTE, _)
default:
// handle
}
}
#undef PASTE
#undef LIMIT
LIMIT
最好为(两者都不会受到伤害)找到好的、自我记录的名称PASTE
,并将上述代码生成限制在一个站点上。
根据大卫的解决方案和您的评论构建:
template<int... Indices>
struct indices {
typedef indices<Indices..., sizeof...(Indices)> next;
};
template<int N>
struct build_indices {
typedef typename build_indices<N - 1>::type::next type;
};
template<>
struct build_indices<0> {
typedef indices<> type;
};
template<int... Indices>
void
bar(int i, indices<Indices...>)
{
static void (*lookup[])() = { &A<Indices>::f... };
lookup[i]();
}
然后调用bar
:bar(i, typename build_indices<N>::type())
哪里N
是你的常数时间常数,sizeof...(something)
。您可以添加一个图层来隐藏该调用的“丑陋”:
template<int N>
void
bar(int i)
{ bar(i, typename build_indices<N>::type()); }
这被称为bar<N>(i)
。
根据您想要做什么(即是否有少量您想要使用的有限实例?)您可以创建一个查找表,然后动态使用它。对于使用选项 0、1、2 和 3 的完全手动方法,您可以执行以下操作:
void bar( int i ) {
static void (*lookup[])(void) = { &A<0>::foo, &A<1>::foo, &A<2>::foo, &A<3>::foo };
lookup[i]();
}
当然,我为示例选择了最简单的选项。如果您需要的数字不是连续的或基于零的,您可能更喜欢 astd::map<int, void (*)(void) >
而不是数组。如果您要使用的不同选项的数量较大,您可能希望添加代码以自动实例化模板,而不是手动输入所有模板......但是您必须考虑模板的每个实例化都会创建一个新的功能,你可能想检查你是否真的需要它。
编辑:我写了一篇仅使用 C++03 功能实现相同初始化的帖子,答案似乎太长了。
Luc Danton在这里写了一个有趣的答案,其中包括使用 C++0x 结构初始化查找表。我不太喜欢该解决方案,因为它将接口更改为需要额外的参数,但这可以通过中间调度程序轻松解决。
不,模板是编译时功能,i
在编译时不知道,所以这是不可能的。A<I>::foo()
应该适应类似的东西A::foo(i)
。
没有
模板实现编译时多态性而不是运行时多态性。
模板参数必须在编译时已知。所以编译器没有办法通过A<i>::foo()
.
如果您想解决问题,那么您还必须制作bar()
一个template
:
template<int i>
void bar() {
A<i>::f(); // ok
}
为此,您必须bar()
在编译时知道参数。