Ich habe eine Klasse namens Writer
, die eine Funktion writeVector
wie folgt hat:
void Drawer::writeVector(vector<T> vec, bool index=true)
{
for (unsigned int i = 0; i < vec.size(); i++) {
if (index) {
cout << i << "\t";
}
cout << vec[i] << "\n";
}
}
Ich versuche, keinen doppelten Code zu haben, während ich mir immer noch Sorgen um die Leistung mache. In der Funktion if (index)
überprüfe ich jede Runde meiner for
Schleife, obwohl das Ergebnis immer das gleiche ist. Dies ist gegen "Sorgen um die Leistung".
Ich könnte dies leicht vermeiden, indem ich den Scheck außerhalb meiner for
Schleife platziere. Ich werde jedoch jede Menge doppelten Codes erhalten:
void Drawer::writeVector(...)
{
if (index) {
for (...) {
cout << i << "\t" << vec[i] << "\n";
}
}
else {
for (...) {
cout << vec[i] << "\n";
}
}
}
Das sind also beide "schlechte" Lösungen für mich. Was ich gedacht habe, sind zwei private Funktionen, von denen eine den Index verlässt und dann die andere aufruft. Der andere übertrifft nur den Wert. Ich kann jedoch nicht herausfinden, wie ich es mit meinem Programm verwenden soll. Ich würde immer noch die if
Prüfung benötigen, um zu sehen, welches ich aufrufen soll ...
Entsprechend dem Problem scheint Polymorphismus eine richtige Lösung zu sein. Aber ich kann nicht sehen, wie ich es hier verwenden soll. Was wäre der bevorzugte Weg, um diese Art von Problem zu lösen?
Dies ist kein richtiges Programm, ich bin nur daran interessiert zu lernen, wie diese Art von Problem gelöst werden sollte.
quelle
Antworten:
Übergeben Sie den Körper der Schleife als Funktor. Es wird zur Kompilierungszeit eingefügt, keine Leistungseinbußen.
Die Idee, Unterschiede weiterzugeben, ist in der C ++ - Standardbibliothek allgegenwärtig. Es wird das Strategiemuster genannt.
Wenn Sie C ++ 11 verwenden dürfen, können Sie Folgendes tun:
Dieser Code ist nicht perfekt, aber Sie bekommen die Idee.
In altem C ++ 98 sieht es so aus:
Auch hier ist der Code alles andere als perfekt, aber er gibt Ihnen die Idee.
quelle
for(int i=0;i<100;i++){cout<<"Thank you!"<<endl;}
: D Dies ist die Art von Lösung, nach der ich gesucht habe, sie funktioniert wie ein Zauber :) Sie könnten sie mit wenigen Kommentaren verbessern (hatten zuerst Probleme, sie zu verstehen), aber ich habe sie bekommen, also kein Problem :)cout << e << "\n";
), würde es immer noch einige Codeduplikationen geben.Wenn dies tatsächlich der Fall ist, hat der Verzweigungsprädiktor kein Problem bei der Vorhersage des (konstanten) Ergebnisses. Dies führt nur zu einem geringen Overhead für Fehlvorhersagen in den ersten Iterationen. In Bezug auf die Leistung besteht kein Grund zur Sorge
In diesem Fall empfehle ich, den Test aus Gründen der Klarheit in der Schleife zu halten.
quelle
std::cout
tun)Um Alis Antwort zu erweitern, die vollkommen korrekt ist, aber dennoch Code dupliziert (Teil des Schleifenkörpers, dies ist bei Verwendung des Strategiemusters leider kaum zu vermeiden) ...
Zugegeben, in diesem speziellen Fall ist die Codeduplizierung nicht viel, aber es gibt eine Möglichkeit, sie noch weiter zu reduzieren. Dies ist praktisch, wenn der Funktionskörper größer als nur ein paar Anweisungen ist .
Der Schlüssel besteht darin, die Fähigkeit des Compilers zu nutzen, eine konstante Faltung / Eliminierung von totem Code durchzuführen . Wir können dies tun, indem wir den Laufzeitwert von manuell einem Wert
index
zur Kompilierungszeit zuordnen (dies ist einfach, wenn nur eine begrenzte Anzahl von Fällen vorliegt - in diesem Fall zwei) und ein beim Kompilieren bekanntes Vorlagenargument ohne Typ verwenden -Zeit:Auf diese Weise erhalten wir kompilierten Code, der Ihrem zweiten Codebeispiel (außen
if
/ innenfor
) entspricht, ohne den Code selbst zu duplizieren. Jetzt können wir die VorlagenversionwriteVector
so kompliziert gestalten, wie wir möchten. Es muss immer nur ein einziger Code verwaltet werden.Beachten Sie, wie die Vorlagenversion (die eine Kompilierungszeitkonstante in Form eines Vorlagenarguments ohne Typ verwendet) und die Nichtvorlagenversion (die eine Laufzeitvariable als Funktionsargument verwendet) überladen sind. Auf diese Weise können Sie je nach Ihren Anforderungen die relevanteste Version auswählen und in beiden Fällen eine ähnliche, leicht zu merkende Syntax verwenden:
quelle
doWriteVector
direkt anrufst, aber ich stimme zu, dass der Name unglücklich war. Ich habe es gerade so geändert, dass es zwei überladenewriteVector
Funktionen hat (eine Vorlage, die andere eine reguläre Funktion), damit das Ergebnis homogener ist. Danke für den Vorschlag. ;)In den meisten Fällen ist Ihr Code bereits gut für die Leistung und Lesbarkeit. Ein guter Compiler kann Schleifeninvarianten erkennen und entsprechende Optimierungen vornehmen. Betrachten Sie das folgende Beispiel, das Ihrem Code sehr nahe kommt:
Ich benutze die folgende Befehlszeile, um es zu kompilieren:
Dann lassen Sie uns die Assembly entleeren:
Die Ergebnisanordnung von
write_vector
ist:Wir können sehen, dass wir zu Beginn der Funktion den Wert überprüfen und zu einer von zwei möglichen Schleifen springen:
Dies funktioniert natürlich nur, wenn ein Compiler erkennen kann, dass eine Bedingung tatsächlich invariant ist. Normalerweise funktioniert es perfekt für Flags und einfache Inline-Funktionen. Wenn die Bedingung jedoch "komplex" ist, sollten Sie Ansätze aus anderen Antworten verwenden.
quelle