Search code examples
c++templatesc++17pointer-to-memberclass-template

Pointer to class member as template argument


How to implement a template that gets a class and a pointer to a member of this class?

I want to template a sort of Accessor of the form

template<??? T, ??? P>
class Accessor
{
    // ...

    T& setMember(const Vector3d& point)
    {
        T& obj = getObject<T>();
        obj.*P = point;
        return obj;
    }
};

Here, there is a class T with a member P, both template variables. I know that P is always of type Vector3d. But I have to stay within C++17. Of course, if you have a C++20 technique... I want to know!

Also, Accessor does not have any reference to a T object. But the function getObject magically handles it here. Therefore do not need to care here.

I'd like to use it like this:

class Accessed
{
    Vector3d one_point;
    Vector3d another_point;

    class OneAccessor : public Accessor<&one_point> {};
    class AnotherAccessor : public Accessor<&another_point> {};
}

Notice the format : public Accessor<&one_point>.

Maybe public Accessor<&Accessed::one_point>.

I would prefer

public Accessor<one_point> // or 
public Accessor<Accessed::one_point>

without the &. I'd like Accessed:: to be inferred.

If you have an answer to my problem that you want to deliver, I would very much like to see it.


Solution

  • template <typename T>
    struct class_type;
    
    template<typename T>
    struct class_type<Vector3d T::*> {
        using type = T;
    };
    
    template<auto P>
    struct Accessor
    {
        using T = typename class_type<decltype(P)>::type;
    
        T& setMember(const Vector3d& point)
        {
            T& obj = getObject<T>();
            obj.*P = point;
            return obj;
        }
    };
    
    class Accessed
    {
        Vector3d one_point;
        Vector3d another_point;
    
        class OneAccessor : public Accessor<&Accessed::one_point> {};
        class AnotherAccessor : public Accessor<&Accessed::another_point> {};
    };
    

    Another version using variables:

    template<typename T>
    struct Accessor
    {
        Vector3d T::* p;
    
        constexpr Accessor(Vector3d T::* p) :p{p} {}
    
        T& setMember(const Vector3d& point)
        {
            T& obj = getObject<T>();
            obj.*p = point;
            return obj;
        }
    };
    
    struct Accessed
    {
        Vector3d one_point;
        Vector3d another_point;
    
        static constexpr Accessor OneAccessor{&Accessed::one_point};
        static constexpr Accessor AnotherAccessor{&Accessed::another_point};
    };