我有
template <int i> struct a { static void f (); };
在代码中的不同位置完成专业化。如何才能为仅在运行时知道的a<i>::f
调用正确的i
void f (int i) { a<i>::f (); } // won't compile
我不想在大i
中列出switch
的所有可能值。
编辑:
我想到了像
这样的东西#include <iostream>
template <int i> struct a { static void f (); };
struct regf {
typedef void (*F)();
enum { arrsize = 10 };
static F v[arrsize];
template < int i > static int apply (F f) {
static_assert (i < arrsize, "");
v[i] = a<i>::f;
return 0;
}
};
regf::F regf::v[arrsize];
template <int i> struct reg { static int dummy; };
template <int i> int reg<i>::dummy = regf::apply<i> ();
void f (int i) { return regf::v[i] (); }
#define add(i) \
template <> struct a<i> : reg<i> { \
static void f () { std::cout << i << "\n"; } \
};
add(1)
add(3)
add(5)
add(7)
int main () {
f (3);
f (5);
}
但它崩溃了(我错过了强制实例化的东西吗?),我不喜欢那个虚拟不是static const
(并且使用内存),当然arrsize
大于必要的。
实际问题:要有一个函数generate (int i)
,只需在运行时调用a<i>::generate ()
来为a<i>
生成类i
的实例。给出了设计(类a<i>
),它们继承自基类,并且可以在代码中的任何地方随时添加更多的a特化,但我不想强迫每个人更改我的{{1手动,因为这可以很容易忘记。
答案 0 :(得分:5)
我不确定这是你能得到的最佳解决方案,因为可能有更好的设计,无论如何你可以使用一些元编程来触发函数的实例化和注册:
// in a single cpp file
namespace {
template <unsigned int N>
int register_a() { // return artificially added
register_a<N-1>(); // Initialize array from 0 to N-1
regf::v[N] = &a<N>::f; // and then N
return N;
}
template <>
int register_a<0>() {
regf::v[0] = &a<0>::f; // recursion stop condition
return 0;
}
const int ignored = register_a<regf::arrsize>(); // call it
}
该代码将实例化函数并注册指向静态成员函数的指针。伪返回类型需要能够在静态上下文中强制执行该函数(通过使用该函数初始化静态值)。
这很容易发生静态初始化惨败。虽然regf::v
没问题,但在静态初始化期间依赖于包含适当指针的regf::v
的任何代码都将失败。您可以使用常规技术改进这一点......
根据您实际发布的点点滴滴,我的猜测是您正在尝试使用抽象工厂,并从每个具体工厂自动注册。有更好的方法来解决问题,但我认为这个答案解决了你的问题(我不确定这是否能解决你的问题)。
答案 1 :(得分:4)
你必须这样做。模板在编译时解析并实例化。除此之外,switch
无需低效。它通常编译成一个查询表,开销很小。
但是,您可以使用递归模板魔术来嵌套if/else
块来替换编译器为您生成的switch
。但普通的switch
应该更具可读性。当然,除非您有数千个案例。
在任何一种情况下,您都需要知道i
在编译时可以拥有的值集,因为编译器需要知道要实例化哪些模板。
答案 2 :(得分:2)
您不能在运行时选择模板特化,它们是在编译时根据定义选择的。
解决您正在查看的调度问题的常用方法是switch
(如您所推测的)或vector
或map
int
的函数指针。
答案 3 :(得分:1)
不,编译器需要在编译时实例化模板,因为它需要在编译时知道i
的值。
答案 4 :(得分:1)
您无法在编译时完成模板实例化。