Шаблонирование C ++ против наследования - PullRequest
7 голосов
/ 06 июля 2011

Я просто понимаю, насколько сложно задавать вопросы ... Надеюсь, я могу привести примеры, которые являются одновременно достаточно точными, чтобы продемонстрировать мою проблему, и достаточно короткими, чтобы не все испортить ... По крайней мере, есть возможность редактировать.

Так что это моя ситуация на данный момент. Конечно, я немного изменил это с точки зрения логики / структуры (и с точки зрения именования в любом случае), пытаясь сосредоточиться на сути моего вопроса:

// MyClass deals with lists (actually several data structures) of the
// type MyType which should support different types and has to be 
// efficiently dealt with. Templating seems just right here
class MyClass
{
  ...

  void doSomething<class MyType>(vector<MyType> someList);

  ...

  // At some point I have to extract elements of the type MyType.
  // The extractor obviously depends on MyType but it is not possible to
  // Create a general version that could use templates itself 
  // (unless I use a specialization for each possible MyType)

  // I am stuck between these two alternatives:

  // Possibility1:
  // Let the client pass the right extractor and template it.
  template<class Extractor, class MyType>
  void extract(const Extractor& extractor, const string& source, 
               vector<MyType>* dest)
  {
     extractor.extract(source, dest);
  }

  // Possibility2:
  // Use a member _extractor of some base type that has to be set
  // to a specialization. The ExtractorBase has a virtual method
  // template<T> void extract(const string& source, vector<T>* myType) = 0
  // with no definition that is only defined in subclasses wrt certain
  // postings.
  ExtractorBase _extractor;

  template<class MyType>
  void extract(const string& source, vector<MyType>* dest)
  {
     _extractor.extract(source, dest);
  }
}

В данный момент я предпочел бы возможность1, потому что мне не нужно связываться с наследованием в Extractor для всех вариантов MyType и связанных с ним Extractor, которые я хочу опробовать в будущем.

С другой стороны, для извлечения может потребоваться сложный код и несколько членов (что-то вроде огромных карт, которые отображают определенные входные данные для определенных значений). Таким образом, не будет никакого увеличения производительности при использовании шаблонов. В частности, использование только заголовочных файлов Extractor и, возможно, даже функторов, которые должны быть встроены, являются чем-то вроде вопроса. В прошлом это было сильным указателем на то, что шаблоны будут только увеличивать сложность кода (необходимость иметь дело с созданием экземпляров, усложнять жизнь клиентскому коду и т. Д.) И что я должен попытаться вообще избежать этого.

Или есть третья возможность, о которой я вообще не думал?

Ответы [ 4 ]

2 голосов
/ 06 июля 2011

Лучше пойти с первой опцией . Это чище и ремонтопригоднее.

Потому что из ваших комментариев я понимаю, что вы делаете неверное предположение для 2-го варианта:

// The ExtractorBase has a virtual method
// template<T> void extract(const string& source, vector<T>* myType) = 0;

NO . Это невозможно; template функция никогда не может быть virtual. Поэтому для реализации 2-го варианта вам нужно выбрать несколько грязных и сложных в обслуживании способов, что не очень хорошая идея.

1 голос
/ 06 июля 2011

У вас есть третий вариант, предоставьте конструктор для MyType, который знает, как создать себя из std::string.Или, еще лучше, пара итераторов, поэтому, если вам нужно создать последовательность MyType s из строки, вы можете использовать диапазон.

1 голос
/ 06 июля 2011

Я вижу первую возможность более гибкой.

Во 2-ой возможности я не вижу интереса ненужного инкапсулирующего экстрактора как члена класса. У вас также больше связей между MyClass и Extractor, что не очень хорошо. шаблонизация уменьшает связь (в некотором роде), поэтому, если у вас есть выбор, она лучше.

0 голосов
/ 06 июля 2011

Это звучит как случай для паттерна стратегии - в вашем классе есть операция, реализация которой может отличаться.

Вот компромиссы, которые я вижу в различных подходах.

В шаблонном решении не нужно объявлять абстрактный класс интерфейса и использовать vtbl, чтобы выяснить, какую реализацию использовать. Но это заставит вас заблокировать приложение во время компиляции.

Решение по наследованию заставит вас объявить абстрактный интерфейсный класс и понизит производительность при поиске реализации в vtbl. Но оно позволит вам выбрать извлекающую реализацию во время выполнения.

Не зная, насколько критически важна производительность для вашего приложения и насколько вам нужно быть гибким, я бы, вероятно, выбрал решение по наследованию, поскольку мне нравится ясность определения интерфейса в абстрактном классе и его кодирования.

...