2
votes

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.

1
I think you mean 'override', not 'overload'...but I think you need a specialization of the whole Array class for the case where Container is a list.sje397
Did you try writing a specialization for the entire class? If you have code that is not specific to the template types you can write a template base class and make your specialized classes inherit from that base.smichak
we have a faq question for this alreadyJohannes Schaub - litb
@JohannesSchaub-litb thanks for the link. Very informative for my problem.kokozul

1 Answers

1
votes

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.