частичная специализация функции в классе шаблона

У меня есть класс шаблона, представляющий массив числовых значений.

Я хочу, чтобы этот класс работал с любым типом числового значения (например, int, double и т. Д.) И тремя типами контейнеров (std :: vector, std :: deque и std :: list).

Вот соответствующие части реализации моей конкретной проблемы:

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;
  }

}

Конечно, компилятор не позволяет мне перегружать оператор [].

Что мне нужно, так это своего рода частичная специализация для operator [], специфичная для std :: list, но частичная специализация функции шаблона не допускается ни в C ++.

(Я знаю, что произвольный доступ к элементам неэффективен для списка, но дело не в этом).

В идеале в клиентском коде я хотел бы использовать класс Array следующим образом:

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;

После долгих исследований я не смог найти рабочего решения.

Что было бы наиболее элегантным способом решить эту проблему?

Спасибо за вашу помощь.


person kokozul    schedule 18.04.2012    source источник
comment
Я думаю, вы имеете в виду «переопределить», а не «перегрузить» ... но я думаю, что вам нужна специализация всего класса Array для случая, когда Container является list.   -  person sje397    schedule 18.04.2012
comment
Вы пробовали написать специализацию для всего класса? Если у вас есть код, не относящийся к типам шаблонов, вы можете написать базовый класс шаблона и сделать так, чтобы ваши специализированные классы унаследовали от этой базы.   -  person smichak    schedule 18.04.2012
comment
у нас уже есть часто задаваемые вопросы по этому поводу   -  person Johannes Schaub - litb    schedule 18.04.2012
comment
См. Здесь: stackoverflow.com/q/1501357/34509   -  person Johannes Schaub - litb    schedule 18.04.2012
comment
@ JohannesSchaub-litb спасибо за ссылку. Очень информативно для моей проблемы.   -  person kokozul    schedule 18.04.2012


Ответы (1)


Чистое решение - использовать полную специализацию шаблонов. Различные специализации могут быть производными от одного общего базового класса, чтобы совместно использовать общий код.

Напишите класс ArrayBase, содержащий весь код, который не зависит от конкретного типа контейнера и который предоставляет доступ к контейнеру, делая его защищенным или делая Array дружественным классом.

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.
}

Другой подход: вы также можете написать статическую функцию-член, содержащую код для доступа к idx-й записи контейнера и специализации этой функции:

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); }
};

Извините, это не работает. Я забыл, что вы не можете специализировать статические функции-члены ... снова.

Другой альтернативой является использование SFINAE, но это неидиоматическое использование, и я бы не рекомендовал его в этом случае.

person Markus Mayr    schedule 18.04.2012
comment
Большое спасибо. Решение 1, вероятно, чище, но решение 2 - это то, что я сейчас ищу. Однако мне пришлось добавить ключевое слово static перед объявлениями функции и изменить template<> на template<Numeric_t> во втором определении функции get. Кроме того, мне интересно, может ли этот подход повлиять на производительность массивов на основе std :: vector по сравнению с исходной реализацией. - person kokozul; 18.04.2012