在编译时解决河内塔
Solving the Towers of Hanoi at compile-time
我正在尝试在编译时解决河内塔,但我发现了一个问题:
template<int src, int dst>
struct move_disc
{
// member access will print src and dst
};
template<int n, int src, int tmp, int dst>
struct hanoi
{
hanoi<n-1, src, dst, tmp> before;
typename move_disc<src, dst>::lol disc;
hanoi<n-1, tmp, src, dst> after;
};
template<int src, int tmp, int dst>
struct hanoi<0, src, tmp, dst>
{
// recursive base case
};
hanoi<3, 1, 2, 3> go;
不幸的是,上面的元程序只打印了六个动作而不是七个:
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<1, 3>’
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<1, 2>’
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<3, 2>’
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<1, 3>’
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<2, 1>’
prog.cpp:11:39: error: no type named ‘lol’ in ‘struct move_disc<2, 3>’
缺少从 1 到 3 的最终移动。为什么?问题能解决吗?
我认为这是因为
hanoi<1, 1, 2, 3>
已经被实例化(给出第一个错误),并且在以后在模板实例化期间"遇到"时不会再次实例化。
[编辑:为了更清楚,这里有一个递归模板实例化的"图"(有错误):
-
hanoi<3, 1, 2, 3>
:- 1:
hanoi<2, 1, 3, 2>
:- 1.1:
hanoi<1, 1, 2, 3>
:- 1.1.1:
hanoi<0, 1, 3, 2>
. - (
move_disc<1, 3>
) - 1.1.2:
hanoi<0, 2, 1, 3>
.
- 1.1.1:
- (
move_disc<1, 2>
) - 1.2:
hanoi<1, 3, 1, 2>
:- 1.2.1:
hanoi<0, 3, 2, 1>
. - (
move_disc<3, 2>
) - 1.2.2:
hanoi<0, 1, 3, 2>
.
- 1.2.1:
- 1.1:
- (
move_disc<1, 3>
) - 2:
hanoi<2, 2, 1, 3>
:- 2.1:
hanoi<1, 2, 3, 1>
:- 2.1.1:
hanoi<0, 2, 1, 3>
. - (
move_disc<2, 1>
) - 2.1.2:
hanoi<0, 3, 2, 1>
.
- 2.1.1:
- (
move_disc<2, 3>
) - 2.2:
hanoi<1, 1, 2, 3>
: 已在 1.1 实例化(move_disc<1, 3>
错误不重复)。
- 2.1:
- 1:
-- 结束编辑。
我能想到的一个"修复"是使每个专用化都独一无二,例如通过添加"id"模板参数(并在递归实例化期间生成唯一的新值):
template<int n, int src, int tmp, int dst, int id>
struct hanoi
{
hanoi<n-1, src, dst, tmp, id*2> before;
typename move_disc<src, dst>::lol disc;
hanoi<n-1, tmp, src, dst, id*2+1> after;
};
template<int src, int tmp, int dst, int id>
struct hanoi<0, src, tmp, dst, id>
{
// recursive base case
};
hanoi<3, 1, 2, 3, 1> go;
直播: http://ideone.com/0lQaXs
我不直接回答你的问题,但这将是我的版本:
#include <type_traits>
using namespace std;
template <typename T> class TD;
struct NullType {};
template <int N, typename S>
struct Stack {
static const int Head = N;
typedef S Tail;
};
#define STACK_0() NullType
#define STACK_1(D1) Stack<D1, NullType>
#define STACK_2(D1, D2) Stack<D1, STACK_1(D2)>
#define STACK_3(D1, D2, D3) Stack<D1, STACK_2(D2, D3)>
#define STACK_4(D1, D2, D3, D4) Stack<D1, STACK_3(D2, D3, D4)>
template <typename S>
struct Pop;
template <int N, typename R>
struct Pop<Stack<N, R>> {
typedef Stack<N, typename Pop<R>::result> result;
};
template<int N>
struct Pop<Stack<N, NullType>> {
typedef NullType result;
};
template <typename S>
struct Top;
template <int N, typename R>
struct Top<Stack<N, R>> {
static const int result = Top<R>::result;
};
template <int N>
struct Top<Stack<N, NullType>> {
static const int result = N;
};
template <typename S, int D>
struct Push;
template <int N, typename S, int D>
struct Push<Stack<N, S>, D> {
typedef Stack<N, typename Push<S, D>::result> result;
};
template <int M>
struct Push<NullType, M> {
typedef Stack<M, NullType> result;
};
template <typename S>
struct SizeOf;
template <int N, typename R>
struct SizeOf<Stack<N, R>> {
static const int value = SizeOf<R>::value + 1;
};
template <>
struct SizeOf<NullType> {
static const int value = 0;
};
template <typename S1, typename S2, typename S3>
struct Hanoi {
typedef S1 Stack1;
typedef S2 Stack2;
typedef S3 Stack3;
template <int I, int J, int unused=0>
struct MoveDisc;
template <int unused>
struct MoveDisc<1, 2, unused> {
typedef Hanoi<
typename Pop<Stack1>::result,
typename Push<Stack2, Top<Stack1>::result>::result,
Stack3
> result;
};
template <int unused>
struct MoveDisc<2, 1, unused> {
typedef Hanoi<
typename Push<Stack1, Top<Stack2>::result>::result,
typename Pop<Stack2>::result,
Stack3
> result;
};
template <int unused>
struct MoveDisc<1, 3, unused> {
typedef Hanoi<
typename Pop<Stack1>::result,
Stack2,
typename Push<Stack3, Top<Stack1>::result>::result
> result;
};
template <int unused>
struct MoveDisc<3, 1, unused> {
typedef Hanoi<
typename Push<Stack1, Top<Stack3>::result>::result,
Stack2,
typename Pop<Stack3>::result
> result;
};
template <int unused>
struct MoveDisc<2, 3, unused> {
typedef Hanoi<
Stack1,
typename Pop<Stack2>::result,
typename Push<Stack3, Top<Stack2>::result>::result
> result;
};
template <int unused>
struct MoveDisc<3, 2, unused> {
typedef Hanoi<
Stack1,
typename Push<Stack2, Top<Stack3>::result>::result,
typename Pop<Stack3>::result
> result;
};
};
static_assert(
is_same<
Pop<STACK_1(1)>::result,
NullType
>::value, "Pop<> not working."
);
static_assert(Top<STACK_2(2, 1)>::result == 1, "Top<> not working.");
static_assert(
is_same<
Push<STACK_1(2), 1>::result,
STACK_2(2, 1)
>::value, "Push<> not working."
);
static_assert(
SizeOf<STACK_4(4, 3, 2, 1)>::value == 4, "SizeOf<> not working."
);
static_assert(
is_same<
typename Hanoi<
STACK_4(4, 3, 2, 1),
STACK_0(),
STACK_0()>::MoveDisc<1,2>::result,
Hanoi<
STACK_3(4, 3, 2),
STACK_1(1),
STACK_0()>
>::value, "MoveDisc<1,2> not working."
);
static_assert(
is_same<
typename Hanoi<
STACK_3(4, 3, 2),
STACK_1(1),
STACK_0()>::MoveDisc<2,1>::result,
Hanoi<
STACK_4(4, 3, 2, 1),
STACK_0(),
STACK_0()>
>::value, "MoveDisc<2,1> not working."
);
static_assert(
is_same<
typename Hanoi<
STACK_1(4),
STACK_0(),
STACK_0()>::MoveDisc<1,3>::result,
Hanoi<
STACK_0(),
STACK_0(),
STACK_1(4)>
>::value, "MoveDisc<1,3> not working."
);
static_assert(
is_same<
typename Hanoi<
STACK_2(3, 2),
STACK_0(),
STACK_2(4, 1)>::MoveDisc<3,1>::result,
Hanoi<
STACK_3(3, 2, 1),
STACK_0(),
STACK_1(4)>
>::value, "MoveDisc<3,1> not working."
);
static_assert(
is_same<
typename Hanoi<
STACK_1(1),
STACK_2(3, 2),
STACK_1(4)>::MoveDisc<2,3>::result,
Hanoi<
STACK_1(1),
STACK_1(3),
STACK_2(4, 2)>
>::value, "MoveDisc<2,3> not working."
);
template <typename H, int D, int F, int T, int V>
struct Solve {
typedef typename Solve<
typename Solve<H, D-1, F, V, T>::result::template MoveDisc<F, T>::result, D-1, V, T, F
>::result result;
};
template <typename H, int F, int T, int V>
struct Solve<H, 1, F, T, V> {
typedef typename H::template MoveDisc<F, T>::result result;
};
template <typename H>
struct Solution {
typedef typename Solve<H, SizeOf<typename H::Stack1>::value, 1, 3, 2>::result result;
};
static_assert(
is_same<
Solution<
Hanoi<
STACK_4(4, 3, 2, 1),
STACK_0(),
STACK_0()
>
>::result,
Hanoi<
STACK_0(),
STACK_0(),
STACK_4(4, 3, 2, 1)
>
>::value, "Solution<> is not working."
);
int main() {
}
堆栈类模板实际上是 TypeList 来自 Andrei Alexandrescu 的《现代C++设计》一书
您的程序是正确的,但您依赖于编译器错误消息(这是实现定义的),在这种情况下,它不会再次打印错误。但在实际代码中,TMP 必须做一些导致运行时行为的事情。如果您只输出(带有std::cout
)src
并在move_disc
构造器中dst
,并使move_disc
成为hanoi
的成员,而不是强制编译时错误,一切正常。
相关文章:
- 为什么在全局范围内使用"extern int a"似乎不行?
- 运行同一解决方案的另一个项目的项目
- 内置函数可查看CPP中的成员变量
- Project Euler问题4的错误解决方案
- Ardunio UNO解决了多个重叠的定时器循环
- 在某些循环内使用vector.push_back时出现分段错误
- 错误:未在此范围内声明'reverse'
- 内联映射初始化的动态atexit析构函数崩溃
- 不同翻译单元中不可重载的非内联函数定义
- 头文件、宏和内联函数c++
- 如何在不到O(N)的时间内解决这个问题?
- 如何在 macOS 卡塔琳娜上解决此错误?
- 如何解决5桩河内塔
- 解决方案在两个日期之间的天数内不起作用
- 如何解决嵌入结构内嵌入的操作员中的歧义
- Visual Studio,在(解决方案资源管理器的)某个筛选器内的所有文件中查找/替换文本
- 你能在C++内联汇编中为我提供更好的解决方案吗
- Visual Studio中的解决方案范围内的生成配置
- 在编译时解决河内塔
- 宏/内联函数解决MSVC10/11中缺少统一初始化式的问题