C++派生类成员指针的持有者类类型推导问题及实现问询
关于成员指针推导派生类类型的问题
我实现了一个以成员指针为模板参数的类,通过辅助类TypeFromMember从成员指针推导持有者类型。但当持有者是派生类、成员来自基类时,即便传入&Derived::node形式的成员指针,推导得到的持有者类型仍是Base。使用GCC编译器,开启-std=c++20标准。
我的疑问:
- 在
TypeFromMember辅助类中,T=Base和T=Derived都符合模板规范,编译器为什么选择匹配T=Base?
- 在
- 如何修改
TypeFromMember的实现,让传入&Derived::node时能正确推导得到Derived而非Base?
- 如何修改
注:我可以手动修改LinkedList的模板参数指定正确的T,但希望辅助类能自动完成推导。
最小可复现代码
#include <type_traits> struct Node{}; class Base { public: Node node; }; class Derived : public Base {}; template <typename T> struct TypeFromMember; template <typename T,typename M> struct TypeFromMember<M T::*> { using ClassType = T; using MemberType = M; }; template<auto PtrToMember> class LinkedList { using T = typename TypeFromMember<decltype(PtrToMember)>::ClassType; public: T *front() { return new T(); // 仅用于验证类型 } }; int main() { LinkedList<&Base::node> bl; LinkedList<&Derived::node> dl; static_assert(std::is_same_v<decltype(bl.front()), Base*>); // 通过 static_assert(std::is_same_v<decltype(dl.front()), Base*>); // 通过 // static_assert(std::is_same_v<decltype(dl.front()), Derived*>); // 失败,推导类型为Base* }
问题1解答
C++中,&Derived::node的实际类型是Node Base::*,而非Node Derived::*。因为node是基类Base的成员,当你通过派生类访问并取其地址时,编译器会自动将其转换为基类的成员指针类型——这是标准规定的行为:成员指针的类型由成员所属的原始类决定,和你书写的访问路径无关。
所以TypeFromMember接收到的模板参数是Node Base::*,自然只能匹配T=Base,根本不存在T=Derived的匹配选项。
问题2解答
由于成员指针本身会丢失派生类的类型信息,仅靠TypeFromMember无法反向推导出Derived,必须额外传递类类型信息。以下是两种可行方案:
方案1:显式传递类类型模板参数
调整LinkedList的模板参数,同时接收类类型T和成员指针:
#include <type_traits> struct Node{}; class Base { public: Node node; }; class Derived : public Base {}; template<typename T, auto PtrToMember> class LinkedList { public: T* front() { return new T(); } }; int main() { LinkedList<Base, &Base::node> bl; LinkedList<Derived, &Derived::node> dl; static_assert(std::is_same_v<decltype(bl.front()), Base*>); // 通过 static_assert(std::is_same_v<decltype(dl.front()), Derived*>); // 通过 }
方案2:用推导指南自动捕获类类型
如果你不想手动指定类类型,可以给LinkedList添加推导指南,让编译器从成员指针的表达式中推导T:
#include <type_traits> struct Node{}; class Base { public: Node node; }; class Derived : public Base {}; template<typename T, auto PtrToMember> class LinkedList { public: T* front() { return new T(); } }; // 推导指南:从成员指针表达式推导出类类型T和成员指针 template<typename T, typename M> LinkedList(M T::*) -> LinkedList<T, &T::node>; int main() { LinkedList<&Base::node> bl; LinkedList<&Derived::node> dl; static_assert(std::is_same_v<decltype(bl.front()), Base*>); // 通过 static_assert(std::is_same_v<decltype(dl.front()), Derived*>); // 通过 }
注意
不存在仅靠成员指针类型就能推导出Derived的方法——因为一个基类成员指针可以指向任意派生类的该成员,两者是一对多的关系,无法从成员指针反向确定唯一的派生类类型。
内容的提问来源于stack exchange,提问作者blackcloud
相关产品推荐
相关产品推荐

