SFINAE 是 C++ 标准中一个有些深奥的特性 —— 它很复杂,并且有许多细微的细节。虽然它通常在手动控制重载解析的背景下被提及,但其主要目的实际上并不是为了实现非常复杂的专家级代码,而是为了让常规的(自动的)重载解析能够按照开发者的预期工作。在这个角色中,通常能完全如预期地工作,且无需额外努力 —— 事实上,开发者通常甚至不需要意识到这个特性的存在。大多数时候,当你编写一个通用重载和一个针对指针的特化重载时,期望后者不会用于非指针类型。大多数时候,可能甚至不会停下来注意到拒绝的重载是不良的 —— 谁在乎呢,它本来就不该使用。但为了确定它不该使用,必须进行类型替换,而这会产生无效代码。SFINAE 解决了这个“先有鸡还是先有蛋”的问题 —— 为了发现重载应该拒绝,必须替换类型,但这会产生本不该编译的代码,而这本不该成为问题,重载本来就应该拒绝,但我们直到替换类型后才知道,如此循环往复。这就是我们所谓的“自然” SFINAE。
当然,我们花了几十页的篇幅,目的并不仅仅是了解到编译器能神奇地做正确的事。SFINAE 更复杂的用法是制造人为的替换失败,从而通过移除某些重载来控制重载解析。本章中,学习了这些最终会因 SFINAE 抑制的临时错误的安全上下文。通过谨慎应用,这种技术可以在编译时检查和区分从类型的基本特征(这是类吗?)到由任意数量 C++ 语言特性提供的复杂行为(有没有办法将这两种类型相加?)的内容。在 C++20 中,通过引入约束和概念,此类代码得到了极大的简化。即使对于为早期标准编写的代码,也可以应用受概念启发的思维方式。
在下一章中,将介绍另一种用于极大增强 C++ 类型层次结构能力的高级模板模式:类继承让能够将信息从基类传递到派生类,而奇异递归模板模式(CRTP)则恰恰相反,它能让基类能够感知到派生类。