Как объявить значение шаблона по умолчанию при использовании CRTP с несколькими параметрами шаблона? - PullRequest
2 голосов
/ 21 сентября 2011

Я хочу сделать:

template <class Derived=BattleData>
class BattleData : public BattleCommandManager<Derived> {
};

Но, очевидно, BattleData не объявлено, поэтому я попробовал предварительное объявление:

template <class T> class BattleData;

template <class Derived=BattleData>
class BattleData : public BattleCommandManager<Derived> {
};

Но тогда я получаю

ошибка: "неправильный номер параметра шаблона во второй строке, с BattleData.

Я действительно не вижу решения этой проблемы!

Редактировать

Причина, по которой я это делаю, заключается в том, что я хочу иметь возможность использовать BattleData непосредственно как class, но я также хочу иметь возможность подкласса его, и в этом случае я должен указать производное class как второй template параметр.

Например, скажем, корпус моего BattleData класса:

template <class Derived> class BattleData: public BaseClass<Derived> {
    void foo1(){};
    void foo2(){};
    void foo3(){};
}

И у меня есть подкласс

template class SubBattleData: public BattleData<SubBattleData> {
    void foo1(){};
}

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

BattleData *x = new BattleData(...);

Я даже не могу сделать следующее, не имея возможности использовать аргументы по умолчанию:

BattleData<BattleData> *x = new BattleData<BattleData>(...);

С одной стороны, причина того, что функции не виртуализируются в классе BattleData, заключается в том, что у них нет виртуальных функций. Другая причина, по которой он не работает для меня, состоит в том, что один из родительских классов CRTP вызывает функции, только если они присутствуют в производном типе (используя decltype(Derived::function) и структуры enable-if like), и в противном случае возвращаются к поведению по умолчанию. , Поскольку может существовать множество таких функций с определенным шаблоном проектирования (например, CRTP, который читает протокол во многих различных случаях и обрабатывает случай определенным образом, только если производный класс задает соответствующую функцию, в противном случае просто передайте его без обработки ).

Таким образом, эти функции могут присутствовать в SubBattleData, а не в BattleData, но оба экземпляра будут работать нормально, если их создать, но невозможно создать экземпляр BattleData.

Ответы [ 4 ]

3 голосов
/ 25 декабря 2012

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

template <class Derived=BattleData <BattleData <BattleData <...>>>
class BattleData : public BattleCommandManager<Derived> {
};

Вы поняли идею. Вместо этого просто используйте заполнитель как void:

template <typename T = void>
class BattleData : public BattleCommandManager <
    typename std::conditional <
        std::is_same <T, void>::value, 
        BattleData <void>,
        T
    >::type>
{
};

Отказ от ответственности: я не скомпилировал выше.

1 голос
/ 21 сентября 2011

Разве вы не можете использовать пустой класс для второго параметра шаблона?

template <class T=DataContainer, class Derived=BattleData<T, Empty> >
class BattleData : public BattleCommandManager<Derived> {
};
1 голос
/ 21 сентября 2011

Я не понимаю, что вы пытаетесь сделать. Что не так с

template <class T=DataContainer>
class BattleData : public BattleCommandManager< BattleData<T> > {
};

Если вы укажете Derived как нечто отличное от фактического производного класса, статический полиморфизм не сработает, и CRTP все равно станет несколько бесполезным.

Изменить: Из того, что я понял, это то, что вы хотите в абстрактных терминах:

template <class Derived> 
struct Base {
    void interface() {
        static_cast<Derived*>(this)->implementation();
    }
};

template<typename T>
struct Derived : Base<Derived> {
  // dummy so we get you example
  T t;
  void implementation() {
    std::cout << "derived" << std::endl;
  }
};

struct Derived2 : public Derived<int> {
  // hide implementation in Derived
  // but still have Base::interface make the right call statically
  void implementation() {
    std::cout << "derived2" << std::endl;
  }
};

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

template<typename Policy>
struct BattleCmdManager : public Policy {
  using Policy::foo;
};

template<typename T>
struct BattleData {
  // ...
protected:
  void foo();
};

struct BattleData2 : public BattleData<int {
  // ...
protected:
  void foo();
};
0 голосов
/ 22 сентября 2011

Вот как я это решил:

template <class Derived> class BattleDataInh: public BaseClass<Derived> {
    void foo1(){};
    void foo2(){};
    void foo3(){};
};

template class SubBattleData: public BattleDataInh<SubBattleData> {
    void foo1(){};
};

class BattleData : public BattleDataInh<BattleData> {
};

И таким образом, я могу добавить любые другие параметры шаблона.Решение все время было у меня на глазах, но я его не видел ...

...