为何typeid无法处理带尾部const的函数类型?
问题分析与解决
报错原因
你说得没错:非成员函数类型不能带有const限定符,void () const是一个无效的类型,这就是编译器报错的直接原因。
为什么会生成这种无效类型?
你的模板remove_ptr_to_member在处理成员函数指针decltype(&container::func)时,把它拆成了两部分:
- 主模板的
T被推导为void () const(因为成员函数指针的形式是T class_t::*,这里的T就是成员函数的"裸类型",包含它的const限定) class_t被推导为container
特化版本直接把T作为type的类型,于是就生成了void () const这个非法的非成员函数类型。
解决方法:移除成员函数类型的const限定
需要修改模板,专门处理带const限定的成员函数指针,将const从函数类型中剥离,生成合法的非成员函数类型。有两种常见方式:
方式1:针对带const的成员函数指针做特化
直接修改你的模板,新增针对const成员函数指针的特化:
template <typename T, typename class_t> struct remove_ptr_to_member { using type = T; }; // 处理普通成员函数指针 template <typename R, typename class_t, typename... Args> struct remove_ptr_to_member<R(class_t::*)(Args...), class_t> { using type = R(Args...); }; // 处理带const的成员函数指针 template <typename R, typename class_t, typename... Args> struct remove_ptr_to_member<R(class_t::*)(Args...) const, class_t> { using type = R(Args...); }; class container { public: void func() const; }; void print_member_type() { std::cout << typeid(typename remove_ptr_to_member<decltype(&container::func), container>::type).name() << '\n'; }
方式2:新增移除函数cv限定的辅助模板
如果需要处理更多cv限定(比如volatile、const volatile),可以先定义一个辅助模板来剥离函数类型的cv限定:
// 辅助模板:移除函数类型的cv限定符 template<typename Func> struct remove_function_cv; // 处理const函数 template<typename R, typename... Args> struct remove_function_cv<R(Args...) const> { using type = R(Args...); }; // 处理volatile函数 template<typename R, typename... Args> struct remove_function_cv<R(Args...) volatile> { using type = R(Args...); }; // 处理const volatile函数 template<typename R, typename... Args> struct remove_function_cv<R(Args...) const volatile> { using type = R(Args...); }; // 原模板修改 template <typename T, typename class_t> struct remove_ptr_to_member { using type = T; }; template <typename T, typename class_t> struct remove_ptr_to_member<T class_t::*, class_t> { using type = typename remove_function_cv<T>::type; }; class container { public: void func() const; }; void print_member_type() { std::cout << typeid(typename remove_ptr_to_member<decltype(&container::func), container>::type).name() << '\n'; }
两种方式都能将type的类型修正为合法的void(),让typeid可以正常处理,编译通过。
内容的提问来源于stack exchange,提问作者Nik Tedig
相关产品推荐
相关产品推荐

