MSVC 19.29无法用std::enable_if和非类型参数特化模板
这是《MSVC fails to deduce template argument》的后续问题:在Visual Studio Enterprise 2019(版本16.11,对应MSVC 19.29)环境下,无论是否启用/permissive-编译选项,均无法编译以下代码。而clang、g++以及MSVC 19.33及更高版本(无需/permissive-)均可正常编译,这是一个典型的SFINAE(替换失败并非错误)相关编译器Bug。
代码的核心目标是为成员变量和成员函数分别实现def()的重载版本(实际业务场景中Prop1/Prop2带有默认值,因此必须通过enable_if进行重载区分,最小复现示例中未体现这部分逻辑):
#include <type_traits> struct Prop1 { constexpr Prop1() {}; }; struct Prop2 { constexpr Prop2() {}; }; constexpr const Prop1 i; constexpr const Prop2 j; struct Klass { template <auto Klass::*MEMBER, const Prop2 &P, typename = std::enable_if_t<std::is_member_object_pointer_v<decltype(MEMBER)>>> void def() {} template <auto Klass::*MEMBER, const Prop1 &P, typename = std::enable_if_t<std::is_member_function_pointer_v<decltype(MEMBER)>>> void def() {} void fn(); int var; }; void Klass::fn() {} int main() { Klass k; k.def<&Klass::fn, i>(); k.def<&Klass::var, j>(); }
问题原因
MSVC 19.29在处理模板参数auto Klass::*MEMBER结合std::enable_if的SFINAE逻辑时存在缺陷:当进行模板重载决议时,编译器没有正确根据std::is_member_object_pointer_v和std::is_member_function_pointer_v的判断结果过滤掉不匹配的重载,导致两个模板都被视为候选,最终触发编译错误。
解决方案
方案1:升级MSVC版本
直接升级到MSVC 19.33及以上版本(对应Visual Studio 2022 17.3及更高版本),该版本已经修复了此SFINAE Bug,无需修改代码即可正常编译。
方案2:兼容旧版本的代码改写
如果必须使用MSVC 19.29,可以通过显式指定成员指针类型的方式绕过Bug,修改后的代码如下:
#include <type_traits> struct Prop1 { constexpr Prop1() {}; }; struct Prop2 { constexpr Prop2() {}; }; constexpr const Prop1 i; constexpr const Prop2 j; struct Klass { // 成员变量版本:显式声明成员指针类型作为模板参数 template <typename MemPtrType, MemPtrType MEMBER, const Prop2 &P> std::enable_if_t<std::is_member_object_pointer_v<MemPtrType>> def() {} // 成员函数版本:显式声明成员指针类型作为模板参数 template <typename MemPtrType, MemPtrType MEMBER, const Prop1 &P> std::enable_if_t<std::is_member_function_pointer_v<MemPtrType>> def() {} void fn(); int var; }; void Klass::fn() {} int main() { Klass k; k.def<decltype(&Klass::fn), &Klass::fn, i>(); k.def<decltype(&Klass::var), &Klass::var, j>(); }
这种方式通过显式传递成员指针的类型,帮助MSVC 19.29正确完成模板参数推导和重载决议,从而避开SFINAE的处理Bug。
内容的提问来源于stack exchange,提问作者Criminal_Affair_At_SO

