我发现在使用 C++ 模板元编程时很难弄清楚我的代码有什么问题。可能是我不太擅长理解错误消息,但据我所知,我不能求助于放入打印语句或断点来弄清楚发生了什么。
除了手动挑选代码并希望它会来找我之外,当你试图弄清楚为什么某些东西没有编译时,你能提供什么提示或建议?
我发现在使用 C++ 模板元编程时很难弄清楚我的代码有什么问题。可能是我不太擅长理解错误消息,但据我所知,我不能求助于放入打印语句或断点来弄清楚发生了什么。
除了手动挑选代码并希望它会来找我之外,当你试图弄清楚为什么某些东西没有编译时,你能提供什么提示或建议?
对于 STL,至少有一些工具可以输出更人性化的错误消息。见http://www.bdsoft.com/tools/stlfilt.html
对于非 STL 模板,您只需了解错误的含义。在你看过他们十几次之后,你就更容易猜出问题出在哪里了。如果您将它们发布在这里,也许有人可以帮助您弄清楚。
您可以尝试使用更新的编译器。如果您使用的是 Visual C++ 6.0,请切换到 9.0,您会发现编译器错误的帮助有了巨大的飞跃。
否则,我的技术通常是测试尽可能少的代码部分,直到找出错误为止。这可能是模板系统最大的失败——没有合理的方法来调试它们。
在您自己的代码中,自由使用编译时断言以使使用问题更容易诊断。
如果您使用的是 gcc,我发现colorgcc可以提供一点帮助。颜色编码可以更容易地在心理上解析出警告、错误和上下文信息。
在元编程语言中做一些复杂的事情时,我多次使用 BOOST_MPL_ASSERT 宏,检查元执行的每一步的结果。Boost.MPL 库对此非常有用。我建议您尽可能多地使用那里的代码,因为它很可能不会包含错误。
当我不太确定正在使用某个类的正确特化时,我倾向于在命名空间中隔离正确的特化。一旦你确定专业化是有效的,你必须确保它被选中。如果不是,您必须找出选择哪一个来代替它。然后我建议使用 Boost.EnableIf 从选择过程中排除这种错误接受的专业化。
最后但并非最不重要的一点是,STLfilt 非常有用,您可以自己修改它,使其尽可能适合您的需求。
但最重要的是尽量不要到处使用元编程。它很复杂,所以只有在真正需要时才使用它。
我认为这应该对您有所帮助。
http://www.bdsoft.com/tools/stlfilt.html
我自己没用过;但是,它可能会对您有所帮助。我还可以告诉你,随着你对模板和元程序有更多的经验,你会习惯错误消息。有时它们可能有点难以阅读;但是,他们的疯狂是有逻辑的。只要让你的终端尽可能大,并在你阅读它时尝试翻译它在你脑海中所说的内容。
您使用的是哪个编译器?VC8 和 9 实际上在输出可读的错误信息方面相当不错。仍然需要一点耐心才能完成,但可以完成,并且它们本质上显示了调用堆栈的编译时等价物。从底部开始,哪个模板实例化导致了错误,模板参数是什么?下一级显示它被实例化的模板等等,一直到顶层。当然,这仅在“输出”选项卡中可见,而不是在编译失败后通常显示的“错误”选项卡中。
GCC 中的原理类似,尽管我上次尝试过,至少格式的可读性有点差。但实际上,您只需要跟踪实例化堆栈,并在每个级别验证它是否使用您期望的类型实例化,直到找到引入错误的类型。
这很痛苦,但可以做到,而且只需要耐心和愿意阅读错误消息即可。:)
此外,通过提供健全性检查,自由使用 static_assert(或 BOOST_STATIC_ASSERT)可以提供很大帮助
对于调试,通常它有助于在某个点停止元程序并显示某种类型计算的结果。这可以这样实现:
template <class T>
struct mp_debug : T::MP_DEBUG_FORCE_COMPILE_FAILURE {};
using Foo = int; // type to be inspected
// usage at namespace scope
template struct mp_debug<Foo>;
// usage at function scope
int main() {
mp_debug<Foo>{};
}
这会导致编译器打印一个编译时错误,显示 mp_debug 的评估类型参数。
随着时间的流逝,您会习惯它,不幸的是,如果您打算使用 C++,则必须这样做。因为,像 VC9 这样的一些库有很好的错误消息,但是一旦你说 GCC 或其他编译器,这些消息就消失了。当你在别人写的某个库中出现错误时,甚至 VC9 也对你没有多大帮助,或者你自己在深夜写的错误,甚至一些 Boost 库也不是那么友好。仅仅是因为并非每个作者都在发生错误时不厌其烦地把事情弄清楚,这在新库中更为常见(往往有最多的错误和较少的帮助)。
此外,您必须记住,您可能会在代码中到处找到漂亮的 STATIC_ERRORS,它们是由作者放置在事情通常会中断的地方,并且总会有可怕的极端情况,这是作者没有想到的,这将产生 400 行错误消息,例如,您在某处错过了 const。
使用工具一开始会帮助你,但从长远来看会伤害你。而且由于这个问题是 C++ 固有的,所以它不会很快消失。在不再使用 C++ 之前,这些错误之墙可能会一直存在。因此,当您需要它们生存时,工具只会割伤您的牙齿。如果你打算很快离开 C++,那么请随意使用它们。现在我通常可以一眼看懂那些 400 行错误信息,所以它们对我来说是一清二楚,但这不是因为任何工具。
与所有事物一样,尤其是 C++,它需要经验和培训。
正如答案已经说明的那样,模板代码基本上有两种困难:1.让它编译,并找到编译器错误的原因 2.让它在运行时做正确的事情
我通常尝试将编译时类型魔法与运行时逻辑分开,这有助于找到问题的原因(类型 1 或 2)。实现这一点的一种方法是使用一种模板类型来实现类型魔法,并使用尽可能少的运行时功能,以及使用模板类型的运行时逻辑的一种普通类型。
如果你然后按照其他回复的建议,特别是关于 compile_time asserts 的建议,将更容易找到问题的根源。
Templight:C++ 模板元程序调试器和探查器
Metashell是一个很好的模板调试工具。带联机模式!