I think this question is related to C++ pointer to member of derived class, but slightly different.
I have a class accepts a pointer-to-member as template parameter, and will deduct the holder's type from pointer-to-member, with the helper described in Inferring type and class when passing a pointer to data member as a non-type template argument.
If the holder is a derived class and the member is from the base class, the deducted type of holder is Base. Even if I pass the pointer-to-member in form Derived::*. The code below is the minimal reproducible example, also available at https://godbolt.org/z/9rEc1876E
I'm using gcc with -std=c++20.
My question is:
In
TypeFromMemberhelper, bothT = BaseandT = Derivedshould match the template specification. How and why the compiler decides to matchT = Base?How can I modify the implementation of
TypeFromMemberso that it can correctly deductDerivedinstead ofBasewhen I actually pass&Derived::nodeas ptr-to-member?
Note: I can modify the LinkedList's template parameters to template<auto PtrToMember, class T = typename TypeFromMember<decltype(PtrToMember)>::ClassType>, and pass correct T manually in such case. I just wonder if the helper can do this automatically.
#include <type_traits>
struct Node{};
class Base
{
public:
Node node;
};
class Derived : public Base {};
// https://stackoverflow.com/questions/65375734/inferring-type-and-class-when-passing-a-pointer-to-data-PtrToMember-as-a-non-type-tem
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(); // just for verify the type
}
};
int main()
{
LinkedList<&Base::node> bl;
LinkedList<&Derived::node> dl;
static_assert(std::is_same_v<decltype(bl.front()), Base*>); // Pass
static_assert(std::is_same_v<decltype(dl.front()), Base*>); // Pass
// static_assert(std::is_same_v<decltype(dl.front()), Derived*>); // Fail, decltype(dl.front()) is Base*
}
&Derived::nodeisNode Base::*, sincenodeis a member ofBase. The standard even has an example exactly like this:struct A { int i; }; struct B : A { }; ... &B::i ... // has type int A::*So there's no way to deduceDerived, asDerivedis not part of the type to begin with.Node Derived::*here, it's just an alias forNode Base::*?Node Derived::*is not an alias for anything. It's a separate type. The point is that the type of&Derived::nodeis notNode Derived::*even though it looks like it should be. It's the same as asking what happened to the float when you write0. Nothing "happened to" the float. The float was never there.