Template a member variable

c++, member-variables, templates

Solution

You can minimize the amount of code that has to be written for each class -- it doesn't have to be a template specialization and it doesn't have to be an entire class.

class LunchBox
{
  public:
    std::vector<Apple> m_apples;
};

class ClassRoom
{
  public:
    std::vector<Student> m_students;
};

// you need one function per type, to provide the member name
auto& get_associated_vector( Student& s ) { return s.m_apples; }
auto& get_associated_vector( ClassRoom& r ) { return r.m_students; }

// and then the decorator is generic
template<typename T>
class accessor_decorator
{
     T& peer;
public:
     auto& getNthElement( int i ) { return get_associated_vector(peer).at(i); }

     auto& takeRandomElement( int i ) { ... }

     // many more ways to manipulate the associated vector

     auto operator->() { return &peer; }
};

LunchBox lunchBox{};
accessor_decorator<LunchBox> lunchBoxWithAccessor{lunchBox};
auto apple3 = lunchBoxWithAccessor.getNthElement(3);

The simple helper function overload should ideally be in the same namespace as the type, to make argument-dependent lookup work (aka Koenig lookup).

It's also possible to specify the member at the point of construction, if you prefer to do that:

template<typename T, typename TMemberCollection>
struct accessor_decorator
{
     // public to make aggregate initialization work
     // can be private if constructor is written
     T& peer;
     TMemberCollection const member;

public:
     auto& getNthElement( int i ) { return (peer.*member).at(i); }

     auto& takeRandomElement( int i ) { ... }

     // many more ways to manipulate the associated vector

     auto operator->() { return &peer; }
};

template<typename T, typename TMemberCollection>
auto make_accessor_decorator(T& object, TMemberCollection T::*member)
     -> accessor_decorator<T, decltype(member)>
{
    return { object, member };
}

LunchBox lunchBox{};
auto lunchBoxWithAccessor = make_accessor_decorator(lunchBox, &LunchBox::m_apples);
auto apple3 = lunchBoxWithAccessor.getNthElement(3);

Problem

Consider the following two classes: ``` class LunchBox { public: std::vector<Apple> m_apples; }; ``` and ``` class ClassRoom { public: std::vector<Student> m_students; }; ``` The classes are alike in that they both contain a member variable vector of objects; however, they are unalike in that the vector's objects are different and the member variables have different names. I would like to write a template that takes either `LunchBox` or `ClassRoom` as a template argument (or some other parameter) and an existing object of the same type (similar to a `std::shared_ptr`). The template would return an object that adds a `getNthElement(int i);` member function to improve accessing the methods. Usage would be like: ``` // lunchBox is a previously initialized LunchBox // object with apples already pushed into m_apples auto lunchBoxWithAccessor = MyTemplate<LunchBox>(lunchBox); auto apple3 = lunchBoxWithAccessor.getNthElement(3); ``` I would like to do this without writing template specializations for each class (which likely would require specifying the member variable to operate on in some way). Preferably, I do not want to modify the `LunchBox` or `ClassRoom` classes. Is writing such a template possible?

Original source