Ich versuche einen bequemen Weg zu finden, um 'pod' C ++ - Strukturen zu initialisieren. Betrachten Sie nun die folgende Struktur:
struct FooBar {
int foo;
float bar;
};
// just to make all examples work in C and C++:
typedef struct FooBar FooBar;
Wenn ich dies bequem in C (!) Initialisieren möchte, könnte ich einfach schreiben:
/* A */ FooBar fb = { .foo = 12, .bar = 3.4 }; // illegal C++, legal C
Beachten Sie, dass ich die folgende Notation explizit vermeiden möchte, da es mir so vorkommt, als würde ich mir den Hals brechen, wenn ich in Zukunft etwas an der Struktur ändere :
/* B */ FooBar fb = { 12, 3.4 }; // legal C++, legal C, bad style?
Um in C ++ dasselbe (oder zumindest ein ähnliches) wie im /* A */
Beispiel zu erreichen, müsste ich einen idiotischen Konstruktor implementieren:
FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar) {}
// ->
/* C */ FooBar fb(12, 3.4);
Was gut zum Kochen von Wasser ist, aber nicht für faule Leute geeignet (Faulheit ist eine gute Sache, oder?). Außerdem ist es so schlecht wie das /* B */
Beispiel, da nicht explizit angegeben wird, welcher Wert an welches Mitglied geht.
Meine Frage ist also im Grunde, wie ich /* A */
in C ++ etwas Ähnliches oder Besseres erreichen kann . Alternativ wäre ich mit einer Erklärung einverstanden, warum ich dies nicht tun möchte (dh warum mein mentales Paradigma schlecht ist).
BEARBEITEN
Mit bequem meine ich auch wartbar und nicht redundant .
quelle
Antworten:
Bestimmte Initialisierungen werden in c ++ 2a unterstützt, aber Sie müssen nicht warten, da sie offiziell von GCC, Clang und MSVC unterstützt werden.
GCC Demo MSVC Demo
quelle
Schon seit
style A
ist in C ++ nicht erlaubt und du willststyle B
dann nicht wie wäre es mitstyle BX
:Zumindest einigermaßen helfen.
quelle
foo
undbar
in Zukunft ein neues Feld einfüge. C würde weiterhin die gewünschten Felder initialisieren, C ++ jedoch nicht. Und das ist der Punkt der Frage - wie man in C ++ das gleiche Ergebnis erzielt. Ich meine, Python macht das mit benannten Argumenten, C - mit "benannten" Feldern, und C ++ sollte hoffentlich auch etwas haben.explicit FooBar::FooBar(int foo, float bar) : foo(foo), bar(bar)
. Beachten Sie das explizite Schlüsselwort. Selbst ein Verstoß gegen den Standard ist in Bezug auf die Sicherheit besser. In Clang: -Wno-c99-ErweiterungenSie könnten ein Lambda verwenden:
Weitere Informationen zu dieser Redewendung finden Sie im Blog von Herb Sutter .
quelle
fb.XXX = YYY
.Extrahieren Sie die Contants in Funktionen, die sie beschreiben (grundlegendes Refactoring):
Ich weiß, dass der Stil dem sehr nahe kommt, den Sie nicht verwenden wollten, aber er ermöglicht das einfachere Ersetzen der konstanten Werte und erklärt sie auch (sodass keine Kommentare bearbeitet werden müssen), falls sie sich jemals ändern.
Eine andere Möglichkeit (da Sie faul sind) besteht darin, den Konstruktor inline zu machen, damit Sie nicht so viel eingeben müssen (Entfernen von "Foobar ::" und Zeitaufwand für das Umschalten zwischen h- und cpp-Datei):
quelle
Ihre Frage ist etwas schwierig, weil sogar die Funktion:
kann genannt werden als:
aufgrund der Regeln für Werbung und Konvertierung für integrierte numerische Typen. (C wurde nie wirklich stark getippt)
In C ++ ist das, was Sie wollen, mit Hilfe von Vorlagen und statischen Zusicherungen erreichbar:
In C können Sie die Parameter benennen, aber Sie werden nie weiter kommen.
Wenn Sie jedoch nur benannte Parameter verwenden möchten, schreiben Sie viel umständlichen Code:
Und wenn Sie möchten, können Sie den Schutz der Typwerbung verbessern.
quelle
Die C ++ - Frontends vieler Compiler (einschließlich GCC und Clang) verstehen die C-Initialisierersyntax. Wenn Sie können, verwenden Sie einfach diese Methode.
quelle
private: FooBar(float x, int y) {};
Ein weiterer Weg in C ++ ist
quelle
inline
!Option D:
FooBar FooBarMake(int foo, float bar)
Legal C, legal C ++. Leicht für PODs optimierbar. Natürlich gibt es keine benannten Argumente, aber das ist wie bei allen C ++. Wenn Sie benannte Argumente möchten, sollte Ziel C die bessere Wahl sein.
Option E:
Legal C, legal C ++. Benannte Argumente.
quelle
FooBar fb = {};
in C ++ verwenden können, werden standardmäßig alle Strukturelemente initialisiert.Ich weiß, dass diese Frage alt ist, aber es gibt eine Möglichkeit, dies zu lösen, bis C ++ 20 diese Funktion endlich von C nach C ++ bringt. Um dies zu lösen, können Sie Präprozessor-Makros mit static_asserts verwenden, um zu überprüfen, ob Ihre Initialisierung gültig ist. (Ich weiß, dass Makros im Allgemeinen schlecht sind, aber hier sehe ich keinen anderen Weg.) Siehe Beispielcode unten:
Wenn Sie dann eine Struktur mit const-Attributen haben, können Sie dies tun:
Dies ist etwas unpraktisch, da Sie Makros für jede mögliche Anzahl von Attributen benötigen und den Typ und Namen Ihrer Instanz im Makroaufruf wiederholen müssen. Sie können das Makro auch nicht in einer return-Anweisung verwenden, da die Asserts nach der Initialisierung erfolgen.
Aber es löst Ihr Problem: Wenn Sie die Struktur ändern, schlägt der Aufruf beim Kompilieren fehl.
Wenn Sie C ++ 17 verwenden, können Sie diese Makros sogar strenger gestalten, indem Sie dieselben Typen erzwingen, z.
quelle
Der Weg
/* B */
ist in C ++ in Ordnung, auch das C ++ 0x wird die Syntax erweitern, so dass es auch für C ++ - Container nützlich ist. Ich verstehe nicht, warum du es schlechten Stil nennst?Wenn Sie Parameter mit Namen angeben möchten, können Sie die Boost-Parameterbibliothek verwenden , die jedoch jemanden verwirren kann, der damit nicht vertraut ist.
Das Neuordnen von Strukturelementen ähnelt dem Neuordnen von Funktionsparametern. Ein solches Refactoring kann Probleme verursachen, wenn Sie es nicht sehr sorgfältig ausführen.
quelle
Was ist mit dieser Syntax?
Gerade auf einem Microsoft Visual C ++ 2015 und auf g ++ 6.0.2 getestet. OK funktionieren.
Sie können ein bestimmtes Makro auch erstellen, wenn Sie das Duplizieren des Variablennamens vermeiden möchten.
quelle
clang++
3.5.0-10 mit-Weverything -std=c++1z
scheint das zu bestätigen. Aber es sieht nicht richtig aus. Wissen Sie, wo der Standard bestätigt, dass dies gültiges C ++ ist?ABCD abc = { abc.b = 7, abc.a = 5 };
.Für mich ist die Verwendung dieses Makros der faulste Weg, eine Inline-Inizialisierung zuzulassen.
Dieses Makro erstellt ein Attribut und eine Selbstreferenzmethode.
quelle