GCC13对noexcept成员函数指针的处理是否变更?如何兼容多编译器?
成员函数指针类归属Trait的跨GCC版本兼容问题
问题描述
实现了一个用于确定成员函数指针所属类的trait:
- 在GCC12中,该trait对
const和const noexcept类型的成员函数指针均能正常工作; - 在GCC13中该trait失效,而Clang 16与MSVC的表现与GCC12一致;
- 若添加
noexcept版本的特化模板,可修复GCC13的问题,但会导致GCC12、Clang及MSVC编译出错。
疑问:该行为是否符合C++标准,还是GCC的bug?若为预期行为,如何同时兼容GCC12与GCC13?
原代码示例:
#include <type_traits> template <auto> struct get_class; template <typename Ret, typename Klass, typename... Args, Ret (Klass::*FN)(Args...) const> struct get_class<FN> : public std::type_identity<Klass> {}; // Uncomment this to fix GCC13, and break everyone else... //template <typename Ret, typename Klass, typename... Args, Ret (Klass::*FN)(Args...) const noexcept> //struct get_class<FN> : public std::type_identity<Klass> //{}; class MyClass { public: int func1() const noexcept {return 0;} int func2() const {return 0;} }; int main(){ [[maybe_unused]]get_class<&MyClass::func1>::type x = MyClass{}; [[maybe_unused]]get_class<&MyClass::func2>::type y = MyClass{}; }
解答
行为合规性说明
GCC13的行为符合C++标准,GCC12的表现属于非标准的宽松实现。
根据C++17及后续标准,noexcept是成员函数类型的一部分:带const noexcept的成员函数指针,与仅带const的成员函数指针属于不同的类型。GCC12及更早版本未严格区分这一点,允许不带noexcept的特化模板匹配带noexcept的成员函数指针;GCC13修正了这一行为,严格遵循标准,因此原特化无法匹配const noexcept类型的成员函数指针,导致编译失败。
而Clang 16和MSVC仍保持宽松处理,允许跨noexcept的模板匹配,这属于编译器的非标准扩展行为。
跨版本兼容方案
通过定义通用的成员函数指针类萃取模板,分别针对带/不带noexcept的const成员函数指针提供特化,再让get_class委托给这个萃取模板,即可兼容所有目标编译器。
兼容代码示例:
#include <type_traits> // 辅助萃取类:提取成员函数指针所属的类 template <typename T> struct extract_class_from_mem_fn; // 特化:处理const非noexcept成员函数指针 template <typename Ret, typename Klass, typename... Args> struct extract_class_from_mem_fn<Ret (Klass::*)(Args...) const> : std::type_identity<Klass> {}; // 特化:处理const noexcept成员函数指针 template <typename Ret, typename Klass, typename... Args> struct extract_class_from_mem_fn<Ret (Klass::*)(Args...) const noexcept> : std::type_identity<Klass> {}; // 主模板:委托给辅助萃取类 template <auto FN> struct get_class : extract_class_from_mem_fn<decltype(FN)> {}; class MyClass { public: int func1() const noexcept {return 0;} int func2() const {return 0;} }; int main(){ [[maybe_unused]]get_class<&MyClass::func1>::type x = MyClass{}; [[maybe_unused]]get_class<&MyClass::func2>::type y = MyClass{}; }
该方案的核心是通过decltype(FN)获取成员函数指针的完整类型,再匹配对应的萃取特化。无论编译器是否严格区分noexcept类型,都能找到正确的特化模板,实现跨版本兼容。
内容的提问来源于stack exchange,提问作者endorph
相关产品推荐
相关产品推荐

