【发布时间】:2010-10-28 20:08:33
【问题描述】:
背景
考虑以下几点:
template <unsigned N>
struct Fibonacci
{
enum
{
value = Fibonacci<N-1>::value + Fibonacci<N-2>::value
};
};
template <>
struct Fibonacci<1>
{
enum
{
value = 1
};
};
template <>
struct Fibonacci<0>
{
enum
{
value = 0
};
};
这是一个常见的例子,我们可以将斐波那契数的值作为编译时常量:
int main(void)
{
std::cout << "Fibonacci(15) = ";
std::cout << Fibonacci<15>::value;
std::cout << std::endl;
}
但你显然无法在运行时获取值:
int main(void)
{
std::srand(static_cast<unsigned>(std::time(0)));
// ensure the table exists up to a certain size
// (even though the rest of the code won't work)
static const unsigned fibbMax = 20;
Fibonacci<fibbMax>::value;
// get index into sequence
unsigned fibb = std::rand() % fibbMax;
std::cout << "Fibonacci(" << fibb << ") = ";
std::cout << Fibonacci<fibb>::value;
std::cout << std::endl;
}
因为 fibb 不是编译时常量。
问题
所以我的问题是:
在运行时查看此表的最佳方法是什么?最明显的解决方案(“解决方案”应该轻描淡写)是有一个大的 switch 语句:
unsigned fibonacci(unsigned index)
{
switch (index)
{
case 0:
return Fibonacci<0>::value;
case 1:
return Fibonacci<1>::value;
case 2:
return Fibonacci<2>::value;
.
.
.
case 20:
return Fibonacci<20>::value;
default:
return fibonacci(index - 1) + fibonacci(index - 2);
}
}
int main(void)
{
std::srand(static_cast<unsigned>(std::time(0)));
static const unsigned fibbMax = 20;
// get index into sequence
unsigned fibb = std::rand() % fibbMax;
std::cout << "Fibonacci(" << fibb << ") = ";
std::cout << fibonacci(fibb);
std::cout << std::endl;
}
但是现在表格的大小是非常硬编码的,将其扩展为 40 并不容易。
我想出的唯一一个具有类似查询方法的是:
template <int TableSize = 40>
class FibonacciTable
{
public:
enum
{
max = TableSize
};
static unsigned get(unsigned index)
{
if (index == TableSize)
{
return Fibonacci<TableSize>::value;
}
else
{
// too far, pass downwards
return FibonacciTable<TableSize - 1>::get(index);
}
}
};
template <>
class FibonacciTable<0>
{
public:
enum
{
max = 0
};
static unsigned get(unsigned)
{
// doesn't matter, no where else to go.
// must be 0, or the original value was
// not in table
return 0;
}
};
int main(void)
{
std::srand(static_cast<unsigned>(std::time(0)));
// get index into sequence
unsigned fibb = std::rand() % FibonacciTable<>::max;
std::cout << "Fibonacci(" << fibb << ") = ";
std::cout << FibonacciTable<>::get(fibb);
std::cout << std::endl;
}
这似乎工作得很好。我看到的唯一两个问题是:
-
调用堆栈可能很大,因为计算斐波那契 需要我们通过 TableMax 一直到 2,并且:
-
如果值在表之外,则返回零而不是计算它。
那么我有什么遗漏吗?似乎应该有更好的方法在运行时挑选出这些值。
可能是 switch 语句的模板元编程版本,它可以生成最多一定数量的 switch 语句?
提前致谢。
【问题讨论】:
-
请问您为什么要这样做? :)
-
好奇心。没有人会因为不编程或不思考而在编程或思考方面变得更好。
-
等等...您希望您的编译时算法使用您在运行时获得的输入来计算结果?这不是需要一台时光机吗?
-
无论如何,你知道的。使用 C++1x,您可以编写 "constexpr int fib(int n) { return (n
-
很好的例子!但有些人只是倾向于用模板做过于聪明的事情。当然,这个案例很有趣,也很有启发性,但对于一个真实的项目,我宁愿把它编码为一个记忆或初始化时创建的查找表。想想看,这个例子并没有减少代码大小,而且增加了一些关于正在发生的事情的模糊性。当然很清楚,因为“斐波那契”这个词很容易解释它的作用。但想象一个更复杂的场景。看到它是一个查找表并非易事。
标签: c++ templates runtime metaprogramming