Gegeben sei eine C ++ - Klasse oder eine Struktur aus einfachen alten Daten, die aus Typen besteht, die operator+
implementieren:
Ist es möglich, Vorlagen oder einen anderen Trick zu verwenden, um den C ++ - Compiler dazu zu bringen, automatisch operator+
zu generieren, die jedes Mitglied hinzufügt, so?
Haftungsausschluss Dies ist mehr oder weniger "automatisch".
Ich hoffe, Sie mögen Vorlagen;) (Auch dies erfordert C ++ 11, vor allem, weil es Tupel und variadische Vorlagen verwendet.)
Außerdem möchte ich Rapptz dafür danken, dass er meine Rekursion / Iteration über Tupel mit dem Indextrick gelöst hat.
Also, das ist eine Mischung aus zwei Ideen, die ich hatte. Der erste ist der, den ich früher in den Kommentaren gepostet habe. Das Problem mit dieser Idee besteht darin, dass Sie alle Ihre Mitgliedsvariablen in ein Tupel setzen müssen, was sehr unpraktisch ist. Ich hatte eine andere Idee, die nicht herauskam, weil es eine zirkuläre Abhängigkeit mit der Adder-Vorlage beinhaltete.
Die letzte Idee ist also, dass Sie von der Addierervorlage erben, die davon ausgeht, dass Sie eine statische Membervariable (vom Typ Tupel) haben, in die Sie Zeiger auf die Membervariablen einfügen, die Sie hinzufügen möchten. Und die Standardimplementierung, die Sie erben, erstellt eine neue Variable vom Typ T, iteriert über den Parameter das Tupel und tut matrixweise an ihnen.
Sie können es in Aktion hier sehen. Beachten Sie, dass Sie Unterstützung für Tostring (oder Operator & lt; & lt;) auf die gleiche Weise wie Operator + (statt manuell, wie ich) und für die Konstruktion aus Initialisierungsliste hinzufügen können.
%Vor%Schließlich, wie in den Kommentaren und von Yakk erwähnt, würde eine wirklich automatische Lösung ein Reflexionssystem erfordern (ähnlich wie C #), aber das ist momentan in C ++ nicht vorhanden.
Nein, dies erfordert (zumindest) die Reflektion zur Kompilierzeit, die Fähigkeit, Code zu schreiben, der die Liste der Membervariablen einer Klasse kennt.
In C ++ gibt es kein solches Konstrukt. Es gibt eine Reihe von Vorschlägen zum Hinzufügen der Compilierungsreflexion in C ++, und sie werden möglicherweise in C ++ 17 angezeigt.
Wenn Sie Ihren Typ so geändert haben, dass die Daten, anstatt in Mitgliedsvariablen gespeichert zu werden, in einer Struktur gespeichert werden, die zur Kompilierzeit reflektiert werden kann, dann kann dies getan werden.
Als Beispiel ist std::tuple
ein geordnetes Tupel typisierter Daten. Die Daten, die in std::tuple
gespeichert sind, können reflektiert werden, und mit ein bisschen Arbeit könnten Operatoren, die das tun, was Sie fragen, geschrieben werden, die funktionieren würden. Wenn Sie Ihre Daten nicht in Mitgliedsvariablen speichern, sondern in einem tuple
entweder durch Vererbung oder durch Komposition speichern, könnte Ihr Problem gelöst werden.
Sie könnten auch Ihre eigene Reflection-Lite schreiben: Wenn Sie eine Member-Funktion schreiben, die std::tie( each member variable in turn )
zurückgibt, würde das Ihnen eine begrenzte Reflexion über den Inhalt Ihrer Struktur geben. Ein operator+
zu schreiben, das in jeder Klasse funktionieren würde, die eine solche Memberfunktion zur Verfügung stellt, wäre nicht schwer.
Nun wird fast so viel Code geschrieben wie beim Schreiben eines tatsächlichen operator+
, aber als Bonus kann dieselbe tie
-Funktion für andere Operationen verwendet werden (sei es <
oder ==
oder -
oder *
). Ich würde empfehlen, CRTP zu verwenden, um zu kennzeichnen, welche Operationen automatisch generiert werden sollen, und dann SFINAE-Operatoren zu verwenden, um die eigentliche Arbeit zu tun, wenn Sie diese Route gehen. Eine Traits-Klasse könnte auch funktionieren (oder CRTP und eine Traits-Klasse verwenden, wobei die Standard-Traits-Klasse das CRTP verwendet, aber Sie können eigene Traps implementieren). Die einzige Sorge, die ich mit einer Lösung der Merkmalsklasse haben würde, ist das Fehlen von ADL bei den Betreibern: Sie müssten sie manuell einziehen, vermute ich?
Als letzte Ausnahme wird es oft als eine gute Idee betrachtet, operator+=
als Memberfunktion zu implementieren und dann eine freie Binärdatei operator+
zu schreiben, die mit +=
implementiert wird.
Wie andere bereits gesagt haben, können Sie dies nicht vollständig automatisch durchführen, da C ++ keine Spiegelung hat. Sie können jedoch eine Menge Funktionalität erhalten, wenn Sie eine Memberfunktion implementieren möchten, die alle Mitgliedszeiger aufruft. Hier ist ein Beispiel, das nicht nur operator+
, sondern auch operator+=
, operator==
und operator<
erzeugt:
Dies kann leicht auf die meisten Dinge ausgedehnt werden, die über alle Mitglieder hinweg funktionieren müssen.
Nein, das ist nicht möglich.
C ++ - Vorlagen sind sehr schwach und es ist zum Beispiel absolut unmöglich, irgendetwas zu tun, was das Aufzählen aller Mitglieder einer Klasse beinhaltet.
Im Laufe der Jahre haben einige sehr schlaue Typen versucht, die C ++ Template-System-Einschränkungen zu umgehen und konnten zum Beispiel simulierte Datenstrukturen (mit Typlisten), simulierte Schleifen (mit Rekursion), simulierte Bedingungen (mit SFINAE) hinzufügen / p>
Tatsache bleibt, dass C ++ - Template-Metaprogrammierung "Template" -Metaprogrammierung ist und nicht mit Kompilierungszeitalgorithmen entworfen wurde, sondern nur als etwas bessere Template-basierte Substitution als C-Makros. Sogar C ++ 11 fügt im Grunde nichts hinzu.
Für einen seltsamen Zirkus-Effekt ("hey Mama, schau mal, was ich überhaupt machen kann") wurde diese Art von Kämpfen populär und selbst jetzt kannst du anscheinend schlaue Jungs finden, die immer noch unzählige Stunden damit verbringen, Metaprogrammierungs-Probleme zu lösen, ohne sie zu benutzen richtige Werkzeuge, nur um zu zeigen, dass sie es können.
Dies kann natürlich nur halb-arbeitende überkomplexe schlechte Lösungen zurückgeben, die die Compiler auch wirklich belasten, und zum Beispiel ist es üblich, dass man statt einer klaren Fehlermeldung Screenfuls von Unsinn brabbelt, wenn man bei diesen spröden Konstruktionen einen Fehler macht / p>
Tatsächlich ist das Problem, das diese komplexen Maschinen zu lösen versuchen, größtenteils nicht-exsitent und nur durch die Wahl einer schlechten Metaprogrammierungssprache selbst auferlegt. Wenn Sie diese Art der Programmierung mögen, wäre es sogar besser, den ganzen Weg in diese Richtung zu gehen und C ++ zu verlassen, um Anwendungen direkt in zu schreiben brainfuck mit Notizblock.
Wenn Sie stattdessen Metaprogrammierungsprobleme wirklich lösen müssen, dann ist wahrscheinlich ein externer C ++ - Generator der richtige Weg (oder Sie können von C ++ in eine Sprache mit ernsthafter Unterstützung für die Metaprogrammierung wechseln).