VS2019与VS2022编译时C++模板偏特化匹配差异问题
VS2019与VS2022模板偏特化匹配差异问题
问题现象
使用VS 2019(平台工具集142)编译以下代码时输出2,但VS 2022(平台工具集143)编译时输出1。需要明确差异原因,并确保两个工具集都能匹配到期望的偏特化(即前两个模板参数相等时,匹配输出2的版本)。
Example.cpp
#include <iostream> template <typename T> class TestB { public: using Type = T; }; template <typename T1, typename T2, typename ... PP> class TestA { public: static void print() { std::cout << "1\n"; }; }; template <typename T1, typename T2, typename P1, typename ... PP> class TestA<T1, T2, P1, PP...> : public TestA<T1, typename P1::Type, PP...> { }; template <typename T1, typename ... PP> class TestA<T1, T1, PP...> { public: static void print() { std::cout << "2\n"; }; }; int main(int, char* []) { TestA<int, int, TestB<int>, TestB<double>>::print(); return 0; }
差异原因
这个差异源于VS2022的143工具集对C++模板偏特化的部分排序规则实现更严格,完全贴合标准要求。
你的代码里有两个偏特化版本:
- 第二个偏特化:
TestA<T1, T2, P1, PP...>,要求至少有一个额外模板参数P1,并通过继承递归展开参数; - 第三个偏特化:
TestA<T1, T1, PP...>,要求前两个模板参数类型相同。
VS2019中,编译器判定第三个偏特化的“特化程度”更高,优先匹配它。但VS2022严格按照标准规则判断:第二个偏特化在参数数量上更具体(固定需要P1参数),而第三个偏特化的“前两参数相同”约束,和第二个的“参数数量”约束无法直接比较优先级,最终编译器选择了第二个偏特化,递归继承后匹配到最基础的通用版本,输出1。
解决办法
要让第三个偏特化在所有工具集中优先匹配,需要明确提升它的特化优先级:通过SFINAE约束让第二个偏特化仅在前两个参数不相等时生效,这样当T1 == T2时,第二个偏特化会被直接排除,编译器自然会选择第三个偏特化。
修改后的代码示例:
#include <iostream> #include <type_traits> template <typename T> class TestB { public: using Type = T; }; template <typename T1, typename T2, typename ... PP> class TestA { public: static void print() { std::cout << "1\n"; }; }; // 添加SFINAE约束,仅当T1和T2类型不同时启用该偏特化 template <typename T1, typename T2, typename P1, typename ... PP> class TestA<T1, T2, P1, PP...> : public TestA<T1, typename P1::Type, PP...>, std::enable_if_t<!std::is_same_v<T1, T2>, void> { }; template <typename T1, typename ... PP> class TestA<T1, T1, PP...> { public: static void print() { std::cout << "2\n"; }; }; int main(int, char* []) { TestA<int, int, TestB<int>, TestB<double>>::print(); return 0; }
也可以把约束写在模板参数列表里,效果相同:
// 另一种约束写法 template <typename T1, typename T2, typename P1, typename ... PP, typename = std::enable_if_t<!std::is_same_v<T1, T2>>> class TestA<T1, T2, P1, PP...> : public TestA<T1, typename P1::Type, PP...> { };
修改后,无论VS2019还是VS2022,只要T1和T2类型相同,都会匹配到输出2的偏特化,行为一致。
内容的提问来源于stack exchange,提问作者Niclas Lindgren
相关产品推荐
相关产品推荐

