Reputation: 41
I have a template class representing an array of numerical values.
I want this class to work for any type of numerical value (e.g. int, double, etc.) and three types of container (std::vector, std::deque, and std::list).
Here are the relevant bits of the implementation for my specific problem :
template < typename Numeric_t, typename Container = std::vector<Numeric_t> >
class Array {
// field member
Container m_data;
// other stuff here
// ...
// random element access for std::vector and std::deque
Numeric_t & operator[] (unsigned int index) { return m_data[index]; }
// random element access for std::list
Numeric_t & operator [] (unsigned int index) {
std::list<Numeric_t> :: iterator it = m_data.begin();
std::advance(it, index);
return *it;
}
}
Of course, the compiler doesn't allow me to overload the operator [].
What I would need is a kind of partial specialization for operator [] specific for std::list, but partial template function specialization is not allowed either in C++.
(I know that random element access is not efficient for a list, but that's not the point here).
Ideally, in the client code I would like to use the Array class like this :
Array < int, std::vector<int> > vec;
Array < int, std::list<int> > lst;
// fill arrays here
// ...
std::cout << vec[0] << std::endl;
std::cout << lst[0] << std::endl;
After lot of research I was not able to find a working solution.
What would be the most elegant way to solve this problem ?
Thanks for your help.
Upvotes: 2
Views: 944
Reputation: 4118
A clean solution is to use full-class template specialization. The different specializations can be derived form one common base class, in order to share common code.
Write a class ArrayBase
containing all the code that does not depend on the particular container type and that grants access to the container, by making it protected or making Array
a friend class.
template <class Numeric_t, class Container>
class Array
: public ArrayBase<Numeric_t, Container>
{
// Container specific code, generic version that works for all containers.
};
template <class Numeric_t>
class Array<Numeric_t, std::vector<Numeric_t>>
: public ArrayBase<Numeric_t, std::vector<Numeric_t>>
{
// Optimized code for std::vector.
}
Another approach: You can also write a static member function containing code to access the idx
-th entry of the container and specialize that function:
template <class Numeric_t, class Container>
class Array
{
template <class Cont>
static Numeric_t get(Cont const& container, unsigned int idx)
{
std::list<Numeric_t>::iterator it = container.begin();
std::advance(it, idx);
return *it;
}
template <>
static Numeric_t get(std::vector<Numeric_t> const& container, unsigned int idx)
{
return container[idx];
}
public:
Numeric_t operator[](unsigned int idx) const { return get(m_data, idx); }
};
I am sorry, this does not work. I forgot that you can't specialize static member functions ... again.
Another alternative is to use SFINAE, but it is a non-idiomatic use of it and I would not recommend it in this case.
Upvotes: 1