Подтвердить что ты не робот

Можно ли использовать полиморфизм шаблонов вместо полиморфизма ОО?

Я пытаюсь придумать шаблонное программирование (и в какой-то момент, метапрограммирование шаблонов) для реальных сценариев. Одна из проблем, которую я нахожу, заключается в том, что шаблоны С++ и полиморфизм не всегда играют вместе так, как я хочу.

Мой вопрос в том, что способ, которым я пытаюсь применить шаблонное программирование, является неправильным (и я должен использовать простой старый ООП), или если я все еще придерживаюсь мышления ООП.

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

Код ООП с использованием композиции:

class Interpolator {
   public:
     Interpolator(ICacheStrategy* const c, IDataSource* const d);
     Value GetValue(const double);
}

void main(...) {
    Interpolator* i;
    if(param==1)
       i = new Interpolator(new InMemoryStrategy(...), new TextFileDataSource(...));
    else if(param==2)
       i = new Interpolator(new InMemoryStrategy(...), new OdbcDataSource(...));
    else if(param==3)
       i = new Interpolator(new NoCachingStrategy(...), new RestDataSource(...));

    while(run) {
       double input = WaitForRequest();
       SendRequest( i->GetValue(input));
    }
}

Версия потенциального шаблона:

class Interpolator<class TCacheStrategy, class TDataSource> {
   public:
     Interpolator();
     Value GetValue(const double);               //may not be the best way but
     void ConfigCache(const& ConfigObject);      //just to illustrate Cache/DS         
     void ConfigDataSource(const& ConfigObject); //need to configured

}

//Possible way of doing main?
void main(...) {
    if(param==1)
       DoIt(Interpolator<InMemoryStrategy,TextFileDataSource>(),c,d);
    else if(param==2)
       DoIt(Interpolator<InMemoryStrategy,OdbcDataSource>(),c,d)
    else if(param==3)
       DoIt(Interpolator<NoCachingStrategy,RestDataSource>(),c,d)

}

template<class T>
void DoIt(const T&  t, ConfigObject c, ConfigObject d) {
   t.ConfigCache(c);
   t.ConfigDataSource(c);
   while(run) {
      double input = WaitForRequest();
      SendRequest( t.GetValue(input));
   }
}

Когда я пытаюсь преобразовать реализацию ООП в реализацию на основе шаблонов, код Interpolator можно перевести без большой боли. В основном замените "интерфейсы" параметрами типа шаблона и добавьте механизм, чтобы либо пройти в экземпляре Strategy/DataSource, либо параметры конфигурации.

Но когда я перехожу к "основному", мне не ясно, как это должно быть написано, чтобы использовать шаблоны в стиле мета-программирования шаблона. Я часто хочу использовать полиморфизм, но он, похоже, не очень хорошо работает с шаблонами (иногда мне кажется, что мне нужны стили для генерации стилей Java... ugh).

Когда я часто нахожу, что хочу сделать, есть что-то вроде TemplateType<?,?> x = new TemplateType<X,Y>(), где x не заботится о том, что X, Y есть.

На самом деле, это часто бывает проблемой при использовании шаблонов.

  • Нужно ли мне применять еще один уровень шаблоны?
  • Я пытаюсь использовать свой блестящий новый ключ для силового шаблона установить гвоздь OOP в слот PCI?
  • Или я просто думаю об этом все неправильно, когда дело касается шаблона программирование?

[Edit] Несколько человек указали, что это не метапрограммирование шаблона, поэтому я немного изменил вопрос. Возможно, эта часть проблемы - я еще не знаю, что такое TMP.

4b9b3361

Ответ 1

Шаблоны предоставляют статический полиморфизм: вы указываете параметр шаблона во время компиляции, реализуя стратегию. Они не обеспечивают динамический полиморфизм, где вы предоставляете объект во время выполнения с виртуальными функциями-членами, которые реализуют стратегию.

В вашем примере шаблона кода создаются три разных класса, каждый из которых содержит весь код Interpolator, скомпилированный с использованием разных параметров шаблона и, возможно, сшивающий их код. Вероятно, это не то, что вы хотите от POV размера кода, хотя в этом нет ничего категоричного. Предполагая, что вы оптимизируете, чтобы избежать накладных расходов на функционирование, возможно, это будет улучшение динамического полиморфизма. Скорее всего, это излишне. Если вы хотите динамически использовать шаблон стратегии, вам не нужны шаблоны, просто делайте виртуальные вызовы там, где это необходимо.

У вас не может быть переменной типа MyTemplate<?> (за исключением появления в другом шаблоне до его создания). MyTemplate<X> и MyTemplate<Y> являются полностью несвязанными классами (даже если связаны X и Y), которые, возможно, просто имеют сходные функции, если они созданы из одного шаблона (что им не нужно - возможно, специализация). Даже если они есть, если параметр шаблона задействован в сигнатурах какой-либо из функций-членов, то эти функции не совпадают, они имеют одинаковые имена. Таким образом, из POV динамического полиморфизма экземпляры одного и того же шаблона находятся в том же положении, что и любые два класса - они могут воспроизводиться только в том случае, если вы даете им общий базовый класс с некоторыми виртуальными функциями-членами.

Итак, вы можете определить общий базовый класс:

class InterpolatorInterface {
public:
    virtual Value GetValue(const double) = 0;
    virtual void ConfigCache(const& ConfigObject) = 0;
    virtual void ConfigDataSource(const& ConfigObject) = 0;
    virtual ~InterpolatorInterface() {}
};

Тогда:

template <typename TCacheStrategy, typename TDataSource>
class Interpolator: public InterpolatorInterface {
    ...
};

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

Btw, это не мета-программирование шаблонов, а просто использование шаблонов.

Изменить. Что касается TMP, здесь канонический вводный пример:

#include <iostream>

template<int N>
struct Factorial {
    static const int value = N*Factorial<N-1>::value;
};

template<>
struct Factorial<0> {
    static const int value = 1;
};

int main() {
    std::cout << "12! = " << Factorial<12>::value << "\n";
}

Заметим, что 12! был рассчитан компилятором и является константой времени компиляции. Это интересно, потому что получается, что система шаблонов С++ является полным языком программирования Turing, который препроцессором C не является. С учетом ограничений ресурсов вы можете выполнять произвольные вычисления во время компиляции, избегая затрат времени на выполнение во время ситуаций, когда вы знаете входы во время компиляции. Шаблоны могут манипулировать параметрами своего шаблона, как функциональный язык, а параметры шаблона могут быть целыми числами или типами. Или функции, хотя они не могут быть "вызваны" во время компиляции. Или другие шаблоны, хотя они не могут быть "возвращены" как статические члены структуры.

Ответ 2

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

class Interpolator
{
public:
    virtual Value GetValue (const double) = 0;
};

template<class TCacheStrategy, class TDataSource>
class InterpolatorImpl : public Interpolator
{
public:
     InterpolatorImpl ();
     Value GetValue(const double);
};

void main()
{
    int param = 1;

    Interpolator* interpolator = 0;

    if (param==1)
        interpolator = new InterpolatorImpl<InMemoryStrategy,TextFileDataSource> ();
    else if (param==2)
        interpolator = new InterpolatorImpl<InMemoryStrategy,OdbcDataSource> ();
    else if (param==3)
        interpolator = new InterpolatorImpl<NoCachingStrategy,RestDataSource> ();

    while (true)
    {
        double input = WaitForRequest();
        SendRequest( interpolator->GetValue (input));
    }
}

Я использую эту идиому довольно часто. Это довольно хорошо скрывает шаблонные вещи от клиентского кода.

Обратите внимание, я не уверен, что это использование шаблонов действительно классов как "метапрограммирование", хотя. Я обычно резервирую этот грандиозный термин для использования более сложных шаблонных приемов времени компиляции, особенно использования условных выражений, рекурсивных определений и т.д. Для эффективного вычисления материала во время компиляции.

Ответ 3

Шаблоны иногда называют статическим (или компиляционным) полиморфизмом, поэтому да, они иногда могут использоваться вместо ООП (динамического) полиморфизма. Конечно, это требует, чтобы типы определялись во время компиляции, а не во время выполнения, поэтому он не может полностью заменить динамический полиморфизм.

Когда я часто нахожу, что хочу сделать, это что-то вроде TemplateType x = new TemplateType(), где x не заботится о том, что X, Y есть.

Да, это невозможно. Вы должны сделать что-то похожее на то, что у вас есть с функцией DoIt(). Зачастую, я думаю, что все равно получается более чистое решение (в итоге вы получаете меньшие функции, которые делают только одну вещь - обычно это хорошо). Но если типы определяются только во время выполнения (как в i в версии OOP вашей основной функции), тогда шаблоны не будут работать.

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