借助CRTP获取C++20重载方法类型并存入元组
在C++20中通过CRTP将重载成员函数指针存入元组
问题描述
我希望在C++20的CRTP模板基类中,自动将派生类中所有名为f的重载成员函数指针存入std::tuple,例如:
template<typename CRTP> struct Template { // 此处写法错误,需要元编程技巧实现 static inline constexpr std::tuple t{&CRTP::f...}; }; struct S : Template<S> { void f(int) { } void f(bool) { } };
期望元组t包含&S::f(int)和&S::f(bool)两个成员函数指针,且该模板能被多个不同派生类复用:
struct S1 : Template<S1> { void f(int) { } void f(bool) { } }; struct S2 : Template<S2> { void f(std::string) { } void f(double) { } void f(std::vector<unsigned>) {} struct SomeOtherType {}; void f(SomeOtherType) {} };
解决方案说明
在标准C20中,编译器无法自动枚举类的所有重载成员函数签名,因此无法直接通过&CRTP::f...生成元组。我们可以通过让派生类显式声明重载签名列表的方式实现需求,这是标准C中最可靠的方案,同时保留模板的复用性。
完整实现代码
#include <tuple> #include <vector> #include <string> // CRTP基类:接受派生类类型和f的重载签名列表 template<typename CRTP, typename... Signatures> struct Template { private: // 辅助模板:将签名转换为成员函数指针类型 template<typename Signature> struct GetMemberPtr; template<typename Ret, typename... Args> struct GetMemberPtr<Ret(Args...)> { using type = Ret(CRTP::*)(Args...); }; // 辅助函数:生成对应签名的成员函数指针 template<typename Ret, typename... Args> static constexpr auto get_ptr(Ret(CRTP::*)(Args...)) { return &CRTP::f; } public: // 生成包含所有重载成员函数指针的元组 static inline constexpr auto t = std::tuple{ get_ptr(static_cast<typename GetMemberPtr<Signatures>::type>(nullptr))... }; }; // 示例派生类1:显式声明f的重载签名 struct S1 : Template<S1, void(int), void(bool)> { void f(int) { } void f(bool) { } }; // 示例派生类2:显式声明f的重载签名 struct S2 : Template<S2, void(std::string), void(double), void(std::vector<unsigned>), void(S2::SomeOtherType)> { struct SomeOtherType {}; void f(std::string) { } void f(double) { } void f(std::vector<unsigned>) {} void f(SomeOtherType) {} }; // 验证代码 int main() { // 验证元组大小符合重载数量 static_assert(std::tuple_size_v<decltype(S1::t)> == 2); static_assert(std::tuple_size_v<decltype(S2::t)> == 4); // 验证成员函数指针类型匹配 using S1Tuple = decltype(S1::t); static_assert(std::is_same_v<std::tuple_element_t<0, S1Tuple>, void(S1::*)(int)>); static_assert(std::is_same_v<std::tuple_element_t<1, S1Tuple>, void(S1::*)(bool)>); }
进阶方案(使用第三方库自动收集)
如果允许使用第三方元编程库(如Boost.Mp11),可以实现自动收集重载签名的功能,无需派生类显式声明。核心思路是利用库的元编程工具检测所有重载的存在并收集签名,再生成对应的成员函数指针元组。
总结
- 标准C++20中,最可靠的方案是让派生类显式声明重载签名列表,基类根据签名生成成员函数指针元组。
- 若需要完全自动收集,需借助第三方元编程库或编译器扩展。
内容的提问来源于stack exchange,提问作者K. Koovalsky
相关产品推荐
相关产品推荐

