2017-01-13 1 views
3

Ich habe zwei Codeauszüge der Vorlage Spezialisierung verwendet, die ich gefunden hatte, dass ich besonders seltsam finde. Ich würde sie sogar unnötig auffällig nennen.C++ Vorlage überbeansprucht

Im Allgemeinen habe ich meine Zweifel, dass Vorlagen tatsächlich die beste Möglichkeit sind, diese Objekte (vor allem den ersten Fall) zu entwerfen.

Welches wäre der bessere Ansatz und warum? Oder gibt es einen ganz anderen Ansatz, der besser ist?

1) Vorlagen als Ersatz des Führens Zeiger auf Funktionen:

//fusion_manager.h 
template <typename InputFilterAlgorithm, 
      typename PredictionAlgorithm, 
      typename AssociationAlgorithm, 
      typename FusionAlgorithm> 
class FusionManager 
{ 
public: 
    FusionManager(Environment& env); 
    ... 
private: 
    Environment& env_m; 
    InputFilterAlgorithm filter_alg_m; 
    PredictionAlgorithm prediction_alg_m; 
    AssociationAlgorithm association_alg_m; 
    FusionAlgorithm fusion_alg_m; 
    ... 
}; 

//fusion_manager.cpp 
template <typename InputFilterAlgorithm, 
      typename PredictionAlgorithm, 
      typename AssociationAlgorithm, 
      typename FusionAlgorithm> 
FusionManager<InputFilterAlgorithm, 
       PredictionAlgorithm, 
       AssociationAlgorithm, 
       FusionAlgorithm, 
       TrackExtendedDataType>::FusionManager(Environment& env) 
     : 
      env_m(env), 
      filter_alg_m(env), 
      prediction_alg_m(env), 
      association_alg_m(env), 
      fusion_alg_m(env) 
{ 
    ... 
} 

//main.cpp 
... 
FusionManager<TestInputFilterAlgorithm, 
       TestPredictionAlgorithm, 
       TestAssociationAlgorithm, 
       TestFusionAlgorithm> fusionManager(env); 
... 

... Statt so etwas wie dieses zu verwenden:

//fusion_manager.h 
class FusionManager 
{ 
public: 
  //Let's say each algorithm is encapsulated by a class 
  FusionManager(Environment& env, 
          InputFilterAlgorithm&&, 
                 PredictionAlgorithm&&, 
                 AssociationAlgorithm&&, 
                 FusionAlgorithm&&); 
private: 
  Environment& env_m; 
    InputFilterAlgorithm filter_alg_m; 
    PredictionAlgorithm prediction_alg_m; 
    AssociationAlgorithm association_alg_m; 
    FusionAlgorithm fusion_alg_m; 
}; 

//fusion_manager.cpp 
FusionManager::FusionManager(Environment& env, 
                     InputFilterAlgorithm&& filter_alg, 
                     PredictionAlgorithm&& prediction_alg, 
                     AssociationAlgorithm&& association_alg, 
                     FusionAlgorithm&& fusion_alg) 
          : 
              env_m(env), 
              filter_alg_m(std::move(filter_alg)), 
              prediction_alg_m(std::move(prediction_alg)), 
              association_alg_m(std::move(association_alg)), 
              fusion_alg_m(std::move(fusion_alg)) 
{ 
  ... 
} 

//main.cpp 
... 
FusionManager<TestInputFilterAlgorithm, 
            TestPredictionAlgorithm, 
            TestAssociationAlgorithm, 
            TestFusionAlgorithm> fusionManager(env); 
... 

2) Die Verwendung von Vorlagen als Ersatz Vererbungs- und virtuelle Methoden:

//factorization.h 
template<typename ProbabilityDistributionType> 
class Factorization 
{ 
... 
public: 
    ProbabilityDistributionType factorize(); 
private: 
    std::Vector<ProbabilityDistributionType> factors_m; 
... 
}; 

//factorization.cpp 
template<> 
CPD Factorization<CPD>::factorize() 
{ 
    for (auto & factor : factors_m) 
    { 
     factor.factorize();//This will call the factorize method of CPD 
    } 
} 

template<> 
JointProbDistr Factorization<JointProbDistr>::factorize() 
{ 
    for (auto & factor : factors_m) 
    { 
     factor.factorize();//This will call the factorize method of JointProbDistr 
    } 
} 

Statt so etwas wie diese zu verwenden:

//factorization.h 
template<typename ProbabilityDistributionType> 
class Factorization 
{ 
... 
public: 
    virtual ProbabilityDistributionType factorize() = 0; 
private: 
    std::Vector<ProbabilityDistributionType> factors_m; 
... 
}; 


//cpd_factorization.h 
class CPDFactorization : public Factorization<CPD> 
{ 
... 
public: 
    CPD factorize();//Implementing the parent's pure virtual method. This will call the factorize method of CPD 
}; 

//jointprobdistr_factorization.h 
class CPDFactorization : public Factorization<JointProbDistr> 
{ 
... 
public: 
    JointProbDistr factorize();//Implementing the parent's pure virtual method. This will call the factorize method of JointProbDistr 
}; 
+3

müssen Sie berücksichtigen, dass nicht immer Vorlagen und Vererbung mit virtuellen Methoden Alternativen sind, weil man zur Kompilierzeit und die andere zur Laufzeit passiert – user463035818

+0

Für Ihren ersten Punkt meinst du statt Zeiger auf eine Schnittstelle? – SirGuy

+0

@ tobi303 Danke, ich bin mir bewusst, dass sie nicht immer austauschbar sind, aber ich bin auf viele Fälle gestoßen, wo sie sind. – alex

Antwort

4

Die Verwendung einer Vorlage hat sowohl den Vor- als auch den Nachteil, dass der Compiler die vollständig spezialisierte Implementierung sieht, in der die Vorlage verwendet wird.

Dies ist ein Vorteil, da es dem Compiler ermöglicht, aggressiver zu optimieren: Es kann beliebig inline eingebunden werden und jegliche Verschiebung von Daten zwischen Funktionsaufrufen aufheben, wobei alle vom Programmierer eingeführten Fehler entfernt werden eine Bemühung, ihren Quellcode auf eine bequeme Weise zu strukturieren.

Dies ist ein Nachteil, da es die Vorlagenentscheidungen zu Kompilierungsentscheidungen zwingt. Wenn eine Variable ein Template-Argument ist, kann sie nicht von der Eingabe zur Laufzeit abhängen. Stellen Sie sich vor, was passieren würde, wenn die Länge von std::string ein Template-Argument wäre: String-Manipulation wäre so flexibel wie in FORTRAN. Du willst das absolut nicht. Natürlich sollten einige Dinge zur Kompilierzeit bekannt sein, und es ist sehr gut, sie als Vorlagenargumente zu haben. Aber wenn Sie Vorlagen übertreiben, erhalten Sie unnötig starren Code. (Gewesen, gemacht, gelernt, es zu vermeiden.)

Dies ist auch ein Nachteil, weil es die Neukompilierung aller Verwendungen einer Vorlage erzwingt, wenn sich ihre Implementierung ändert. Wenn Ihr Programm alle Vorlagen außer Main, ist, müssen Sie alles für jede kleine Änderung neu kompilieren. Wenn Sie Zeiger auf Funktionen, virtuelle Funktionen und/oder Funktoren verwenden, müssen aufrufende Sites nicht neu kompiliert werden, wenn sich die Implementierung der aufgerufenen Funktion ändert. In einem ordnungsgemäßen Setup-Build-System wären sie von der Kopfzeile abhängig, die sich nur ändern würde, wenn die Schnittstellenänderungen unterbrochen würden.Die Folge davon ist für mich, dass alle meine Templates kleine, eigenständige Code-Teile sein sollten, die nicht von anderen Templates in mehreren Layern abhängig sind.

Alles in allem sind Vorlagen ein großartiges Werkzeug und gleichzeitig ein sehr einfach zu benutzendes Werkzeug. Versuchen Sie, sie nicht zu benutzen, wenn Sie keinen wirklichen Vorteil daraus ziehen.

+1

Vielen Dank. Ich habe irgendwie den Eindruck gewonnen, dass die Verwendung von verschachtelten Vorlagenstrukturen irgendwie * im Stil * ist. Wenn Sie sich zum Beispiel die Eigen-Bibliothek ansehen, werden Sie eine starke Verwendung der Vorlage sehen, die sie fast unlesbar macht. Gleichzeitig ist es eine der meistbenutzten und angesehensten Bibliotheken in ihrem Bereich, daher nahm ich an, dass es sozusagen richtig gemacht werden muss. – alex

+0

@alex Ja, ich habe den gleichen Eindruck: Es gibt ziemlich viele Leute da draußen, die scheinbar religiös sind, wenn es darum geht, Vorlagen überall zu verwenden, und viele von ihnen scheinen im Kontext von Standardbibliotheken zu funktionieren. Ich verstehe wirklich nicht, wie sie damit weitermachen können, und zwar auf einer so verblüffenden Ebene, wo sie im Grunde genommen eine andere Sprache als C++ - Vorlagen implementieren und niemals darüber nachdenken, eine weitere Ebene von Vorlagen hinzuzufügen. Meine Erfahrung zeigt, dass ich nicht religiös über so ziemlich alles, was mit der Programmierung zu tun hat, geworden bin, deshalb vermeide ich es, Bibliotheken zu verwenden, die sich zu sehr auf Vorlagen verlassen. – cmaster

3

Die erste kann aufrufbar mit irgendetwas verwendet werden - Funktionszeigern, std::function usw.
Ihr Vorschlag ist sehr begrenzt Typ-weise.

Der zweite vermeidet virtuelle Aufrufe, was in Situationen nützlich ist, in denen Sie das vermeiden möchten, und bietet mehr Möglichkeiten für Inlining.

Kurz gesagt: Die erste verwendet Vorlagen für die Flexibilität, die zweite für die Leistung.