You said:
I would like to do this without writing specialized templates for each class
I am not sure why this is a limitation. It is unclear what else you are not allowed to use.
If you are allowed to use multiple function overloads, you can get what you want.
std::vector<Apple> const& getObjects(LunchBox const& l) { return l.m_apples; } std::vector<Student> const& getObjects(ClassRoom const& c) { return c.m_students; }
You can write generic code that works with both LaunchBox and ClassRoom without writing any other specialties. However, overloading recording functions is a form of specialization.
Another option would be to upgrade LaunchBox and ClassRoom with
class LunchBox { public: std::vector<Apple> m_apples; using ContainedType = Apple; }; class ClassRoom { public: std::vector<Student> m_students; using ContainedType = Apple; };
and then take advantage of the fact that
LaunchBox b; std::vector<Apple>* ptr = reinterpret_cast<std::vector<Apple>*>(&b);
is a legal construct. Then the next class will work fine.
template <typename Container> struct GetElementFunctor { using ContainedType = typename Container::ContainedType; GetElementFunctor(Container const& c) : c_(c) {} ContainedType const& getNthElement(std::size_t n) const { return reinterpret_cast<std::vector<ContainedType> const*>(&c_)->operator[](n); } Container const& c_; };
and you can use it like:
LunchBox b; b.m_apples.push_back({}); auto f = GetElementFunctor<LunchBox>(b); auto item = f.getNthElement(0);
source share