SFINAE类模板特化匹配规则疑问:enable_if参数修改为何影响匹配?
我在学习类/结构体模板特化的SFINAE机制时,对一个细节案例的匹配规则感到困惑。初始代码中,Foo<int>会匹配特化版本输出"specialized";但将特化模板中std::enable_if的第二个模板参数改为int后,却匹配了通用版本输出"general",想请教该修改导致匹配优先级变化的原因。
初始代码
#include <iostream> template <typename T, typename = void> struct Foo{ void foo(T t) { std::cout << "general"; } }; template <typename T> struct Foo<T, typename std::enable_if<std::is_integral<T>::value>::type> { void foo(T t) { std::cout << "specialized"; } }; int main() { Foo<int>().foo(3); return 0; }
修改后的特化模板代码
template <typename T> struct Foo<T, typename std::enable_if<std::is_integral<T>::value, int>::type> { void foo(T t) { std::cout << "specialized"; } };
原因解析
要弄明白这个问题,关键得搞懂类模板特化的匹配规则和std::enable_if的工作方式:
初始代码的匹配逻辑
通用模板的第二个模板参数默认是void,所以Foo<int>其实是Foo<int, void>的简写。
特化版本里,std::enable_if的默认第二个参数就是void,当T是整数类型时,std::enable_if<std::is_integral<T>::value>::type等价于void。这个特化版本本质是针对Foo<T, void>的特化(附加了T为整数的条件),和Foo<int, void>完全匹配,所以特化版本优先级更高,被选中执行。
修改后的匹配逻辑
当把enable_if的第二个参数改为int后,只要T是整数类型,特化版本的第二个模板参数就变成了int。
但你调用的Foo<int>依旧是Foo<int, void>(通用模板的默认参数没变),这个实例和特化版本的Foo<T, int>参数类型不匹配,特化版本无法被选中,只能匹配通用版本,所以输出"general"。
总结:类模板特化要生效,必须和主模板实例化后的所有模板参数完全对应。初始时特化的第二个参数和主模板默认参数一致,所以能匹配;修改后参数类型不匹配,特化就失效了。
内容的提问来源于stack exchange,提问作者Yilie Ma

