带默认参数的SFINAE在MSVC可编译,GCC/Clang不行,求标准判定
标准判定:MSVC与GCC/Clang在SFINAE场景下的行为正确性
我学习C++的SFINAE技术后编写了一段程序,该程序可在MSVC中正常编译运行,但无法通过GCC和Clang的编译。
程序代码如下:
#include <iostream> #include <type_traits> struct C1{ enum{ c1 = 1 }; }; struct C2{ enum{ c2 = 5 }; }; template<class C> class entity{ void func1(){std::cout << "func1 called";} void func2(){std::cout << "func2 called ";} public: template<typename T = C>void common(bool b = std::is_same<T, C1>::value && std::is_enum<decltype(T::c1)>::value) { func1(); } template<typename T = C>void common(int, bool b = std::is_same<T, C2>::value && std::is_enum<decltype(T::c2)>::value) { func2(); } template<typename... T> void common(T...) { std::cout <<" General version " << std::endl; } }; int main() { entity<C2> e; e.common(); //works with msvc but not with gcc and clang }
调用e.common()时,MSVC可正常处理,但GCC报错如下:
In member function 'void entity<C>::common(bool) [with T = C2; C = C2]': <source>:21:102: error: 'c1' is not a member of 'C2' 21 | template<typename T = C>void common(bool b = std::is_same<T, C1>::value && std::is_enum<decltype(T::c1)>::value) | ^ <source>:41:13: note: when instantiating default argument for call to 'void entity<C>::common(bool) [with T = C2; C = C2]' 41 | e.common(); //works with msvc but not with gcc and clang | ~~~~~~~~^~
标准判定结论
GCC和Clang的行为符合C++标准,MSVC的行为不符合标准。
依据C++标准,SFINAE(替换失败并非错误)仅适用于函数模板的模板参数推导阶段:当推导模板参数时,若替换模板参数到函数的签名(返回类型、参数类型)中出现无效构造,会将该候选函数从重载集中排除,而非引发编译错误。
但函数模板的默认参数的实例化并不属于SFINAE的适用范围:在本例中,调用e.common()时,第一个common模板的模板参数T会被推导为C2,此时需要实例化该函数的默认参数表达式std::is_same<T, C1>::value && std::is_enum<decltype(T::c1)>::value,其中decltype(T::c1)在T=C2时是非法的(C2没有c1成员)。这个错误发生在模板参数推导完成之后,属于默认参数的实例化错误,会直接引发编译错误,而非通过SFINAE排除该候选函数。因此GCC和Clang的报错是符合标准要求的,MSVC允许该代码编译的行为不符合标准。
内容的提问来源于stack exchange,提问作者user20562802
相关产品推荐
相关产品推荐

