4 Stimmen

Wie deklariert man einen Standardwert für ein Template, wenn man CRTP mit mehreren Template-Parametern verwendet?

Ich möchte folgendes tun:

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

Aber offensichtlich ist BattleData nicht deklariert, also habe ich eine Vorwärtsdeklaration versucht:

template <class T> class BattleData;

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

Aber dann bekomme ich

Fehler: "Falsche Anzahl von Vorlagenparametern in der zweiten Zeile mit BattleData."

Ich sehe wirklich keine Lösung dafür!

Bearbeiten:

Der Grund, warum ich das mache, ist, weil ich BattleData direkt als Klasse verwenden möchte, aber ich möchte es auch als Unterklasse verwenden können, wobei ich die abgeleitete Klasse als zweiten Template-Parameter angeben muss.

Zum Beispiel, angenommen der Hauptteil meiner BattleData-Klasse ist:

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

Und ich habe eine Unterklasse

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

Ich möchte trotzdem in einigen Fällen Code wie diesen schreiben können:

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

Ich kann nicht einmal folgendes tun, ohne Standardargumente verwenden zu können:

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

Einerseits ist der Grund, warum Funktionen in der BattleData-Klasse nicht virtualisiert sind, der Vorteil, keine virtuelle Funktion zu haben. Der andere Grund, warum es für mich nicht funktioniert, ist, dass eine der Eltern-CRTP-Klassen Funktionen nur aufruft, wenn sie im abgeleiteten Typ vorhanden sind (mit decltype(Derived::function) und enable-if-ähnlichen Strukturen) und andernfalls auf Standardverhalten zurückgreift. Da es eine große Anzahl dieser Funktionen mit einem bestimmten Designmuster geben kann (wie ein CRTP, das ein Protokoll mit vielen verschiedenen Fällen liest und einen Fall nur auf eine bestimmte Weise verarbeitet, wenn die abgeleitete Klasse die entsprechende Funktion spezifiziert, sonst nur transferiert, ohne zu verarbeiten).

Also können diese Funktionen in SubBattleData vorhanden sein und nicht in BattleData, aber beide Klassen würden gut funktionieren, wenn instanziiert, aber es ist unmöglich, BattleData zu instanziieren.

2voto

anon77123 Punkte 21

Sie sollten Ihre ursprünglichen Designziele natürlicher erreichen können als oben. Sie können den tatsächlichen abgeleiteten Typ als Standard klar nicht verwenden, weil Sie wirklich folgendes schreiben wollen:

template >>
class BattleData : public BattleCommandManager {
};

Sie verstehen schon. Verwenden Sie stattdessen einfach einen Platzhalter wie void:

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

Haftungsausschluss: Ich habe das oben genannte nicht kompiliert.

1voto

pmr Punkte 56454

Ich verstehe nicht, was du versuchst zu tun. Was ist falsch an

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

Wenn du Derived so spezifizierst, dass es etwas anderes als die tatsächlich abgeleitete Klasse ist, funktioniert die statische Polymorphie nicht und CRTP wird sowieso irgendwie sinnlos.

Bearbeitung: Nach meiner Kenntnis ist dies, was du in abstrakten Begriffen tun möchtest:

template  
struct Base {
    void interface() {
        static_cast(this)->implementation();
    }
};

template
struct Derived : Base {
  // Dummy, damit wir dein Beispiel haben
  T t;
  void implementation() {
    std::cout << "abgeleitet" << std::endl;
  }
};

struct Derived2 : public Derived {
  // Implementierung in Derived verbergen
  // aber Base::interface den richtigen Aufruf statisch machen lassen
  void implementation() {
    std::cout << "abgeleitet2" << std::endl;
  }
};

Es gibt keine Möglichkeit, die mir bekannt ist, wie du dies zum Laufen bringen kannst. Ein anderer Ansatz wäre die Verwendung von Richtlinienklassen anstelle von CRTP. Sie sind mit Vererbung kompatibel und du kannst ein ähnliches Verhalten erreichen.

template
struct BattleCmdManager : public Policy {
  using Policy::foo;
};

template
struct BattleData {
  // ...
protected:
  void foo();
};

struct BattleData2 : public BattleData

1voto

Tio Pepe Punkte 3031

Können Sie nicht eine leere Klasse für den zweiten Vorlagenparameter verwenden?

template  >
class BattleData : public BattleCommandManager {
};

0voto

coyotte508 Punkte 8222

Hier ist, wie ich es gelöst habe:

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

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

class BattleData : public BattleDataInh {
};

Und auf diese Weise kann ich auch andere Template-Parameter hinzufügen. Die Lösung war die ganze Zeit vor meinen Augen, aber ich habe sie nicht gesehen...

CodeJaeger.com

CodeJaeger ist eine Gemeinschaft für Programmierer, die täglich Hilfe erhalten..
Wir haben viele Inhalte, und Sie können auch Ihre eigenen Fragen stellen oder die Fragen anderer Leute lösen.

Powered by:

X