Seitdem es die sog. uniform-initialization Syntax (aka
brace-initialization) gibt, verwende ich sie konsequent. Dies hilft m.E.
sehr, gerade auch einem Neuling den Unterschied zwischen Initialisierung
und Zuweisung klar zu machen:
1
intx{};
2
doubley{1.0};
3
Az{42];
4
5
x=1;
6
y=2.0;
7
8
for(autoi{size()};i-->0;){
9
}
10
11
std::array<int,3>a{1,2,3};
12
13
structAG{
14
doublem1{};
15
doublem2{};
16
};
17
18
AGag{1.0,2.0};
19
20
structX{
21
explicitX(intv):m{v}{}
22
intm{};
23
inlinestaticconstintm2{42};
24
};
Das uniform bezieht sich ja darauf, dass ich so ziemlich alles mit
dieser Syntax initialisieren kann, und es passiert das "Richtige" (s.a.
übliches mit Beispiel std::vector oder Narrowing).
In meiner "Blase" ist das seit Jahren so üblich, ich sehe es auch seit
langem in vielen Vorträgen. Allein in den Beispielen, die in diesem
Forum gepostet werden. sieht man es selten bis nie.
Woran liegt das? Das die geschweiften Klammern so ungünstig auf der
deutschen Tastatur liegen?
Wie sind Eure Erfahrungen damit?
Wilhelm M. schrieb:> In meiner "Blase" ist das seit Jahren so üblich, ich sehe es auch seit> langem in vielen Vorträgen. Allein in den Beispielen, die in diesem> Forum gepostet werden. sieht man es selten bis nie.> Woran liegt das?
Weil es (vermutlich) in 99,999999% des real existierenden
Produktionscode auch nicht gemacht wurde/wird.
Wilhelm M. schrieb:> Dies hilft m.E.> sehr, gerade auch einem Neuling den Unterschied zwischen Initialisierung> und Zuweisung klar zu machen
Ganz ehrlich, wer das C++ professionell einsetzt, sollte solche
Banalitäten hinter sich gelassen haben.
Oliver
Imho ein schwieriges Thema bei dem sich nicht einmal die Experten einig
sind. Nicolai Josuttis gibt regelmäßig Talks in denen er zu {} überall
rät. Timur Doumlers Mantra ist "() und = überall, {} wos nicht anders
geht".
Ich persönlich gehöre zum 1.Lager. Ich nutze {} überall, () wos nicht
anders geht und = einfach gar nicht. Gäbe es ein Compiler-Flag der die =
Initialisierung als Error melden würde, so würde ich es vermutlich
nutzen.
Auch als "Profi" halte ich den sofort ersichtlichen Unterschied zw.
Initialisierung und Zuweisung nicht für eine Banalität.
Wilhelm M. schrieb:> Woran liegt das? Das die geschweiften Klammern so ungünstig auf der> deutschen Tastatur liegen?
Ich würde sagen weil viele noch C parallel zu C++ benutzen. Und
verwenden eine einheitliche Schreibweise.
Zu den vielen Initialisierungssyntaxen¹, von denen keine überall
verwendbar ist, kam mit der Brace-Initialization halt noch eine weitere
hinzu, die wieder nicht überall verwendbar ist und zudem für numerische
Datentypen wie int, double usw. auch noch extrem hässlich aussieht.
Das ist einer der vielen Punkte, wo ich mir ein "C++2" wünschen würde,
bei dem auf Kosten der Abwärtskompatibilität sämtliche Altlasten über
Bord geworfen werden und die guten Features von C++ so überarbeitet
werden, dass sie in sich konsistent und durchgängig verwendbar sind.
Damit entstünde eine neue, aber insbesondere für C++-Programmierer
schnell zu erlernende Sprache mit deutlich reduziertem Sprachumfang, der
dann sogar wieder Platz für sinnvolle Erweiterungen böte.
Dieses C++2 hätte dann auch nur eine einzige Initialisierungssyntax
statt derer drei oder mehr.
Zur eigentlichen Frage:
Die Brace-Initialization verwende ich gerne für Klassen, die Container-
Charakter und einen Konstruktor mit einem initializer_list-Parameter
haben (wie bspw. std::vector). Ich bevorzuge dabei folgende Syntax:
1
Foofoo={1,2,3,4};// mit Gleichheitszeichen
Geht das nicht, weil bspw. der Konstruktor als explicit deklariert ist:
1
Foofoo({1,2,3,4});// mit Konstruktorargument in runden
2
// Klammern
Folgende Syntax verwende ich nicht (und brauche sie hoffentlich auch
nie):
1
Foofoo{1,2,3,4};// leicht gruselig
Für alle anderen Klassen bzw. Konstruktoren:
1
Barbar(1,'a');// mit Konstruktorargumenten in runden Klammern
Für Datentypen, die keine Klassen sind (int, double usw.) verwende ich
bevorzugt
1
intn=5;// mit Gleichheitszeichen, ohne geschweifte Klammern
Bei der Initialisierung von Membervariablen geht das nicht, deswegen
dort:
1
structC{
2
C():n(5){}// mit Pseudokonstruktorargument in runden Klammern
3
intn;
4
};
Folgende Syntax verwende ich nicht (und brauche sie hoffentlich auch
nie):
1
intn{5};// stark gruselig
Natürlich geht mir dadurch das Narrowing verloren. Wenn aber überall
sonst im Programm ungewollte (und sogar unvermeidbare) implizite
Typkonvertierungen stattfinden, stören sie mich an dieser Stelle auch
nicht. Ganz im Gegenteil würde es mich befremden, wenn ausschließlich
bei der Initialisierung eine andere Typprüfung stattfände als sonstwo.
Das ist auch so ein Fall, wo eine kleine Detailverbesserung eingeführt
wurde, die aber überhaupt nicht in das Gesamtkonzept der Sprache passt,
so dass man auch gleich darauf verzichten kann.
Zusammenfassend verwende ich also zwei Initialisierungssyntaxen:
1. mit Gleichheitszeichen
2. wenn (1) nicht möglich ist, mit Konstruktorargument(en) in runden
Klammern
jeweils ggf. mit geschweiften Klammern zur Gruppierung mehrerer
Einzelinitialisierer zu einer Initialisiererliste.
Die Alternative mit ebenfalls zwei Initialisierungssyntaxen wäre:
1. Brace-Initialization ohne Gleichheitszeichen
2. wenn (1) nicht möglich ist, mit Konstruktorargument(en) in runden
Klammern
Welche dieser beiden Alternativen bevorzugt wird, ist IMHO (abgesehen
von dem fragwürdigen Narrowing) reine Geschmackssache. Ich persönlich
favorisiere ganz klar die erste, weil sie weniger gruselig aussieht und
der Kontrast bei der Verwendung zusammen mit Legacy-Code nicht so groß
ist.
Wilhelm M. schrieb:> for(int i{}; ; ) {> }
Pfui Deifel, das toppt sogar noch meine "gruselig"-Beispiele von oben :)
——————————————
¹) In wohl keiner anderen Sprache gibt es auch nur annähernd so viele
wie in C++.
Anfänger oder Benutzer anderer Sprachen werden durch die wirre Syntax
"for ( int i{}; ... )" mit absoluter Sicherheit mehr verwirrt als dass
es ihnen hilft.
Bei C++ gilt noch viel mehr als bei anderen Programmiersprachen:
pragmatisch sein. Code schreiben, der möglichst intuitiv lesbar ist,
auch wenn der Leser nicht mit jedem skurrilen Detail der Sprache
vertraut ist. Bei Konstrukten, die sich unintuitiv verhalten, hilft oft
auch einfach ein Kommentar.
An jeder nur möglichen Stelle die unüblichste oder unlesbarste
Problemlösung zu wählen, weil es theoretischen Vorteile bietet oder
Coolness demonstriert hilft niemandem.
Entsprechend pragmatisch verwende ich Brace-Initializer eigentlich
hauptsächlich zur Initialisierung von Vektoren oder seltener Structs.
Vincent H. schrieb:> Imho ein schwieriges Thema bei dem sich nicht einmal die Experten einig> sind.
Genau deswegen habe ich hier auch einmal nachgefragt.
> Ich persönlich gehöre zum 1.Lager. Ich nutze {} überall, () wos nicht> anders geht und = einfach gar nicht. Gäbe es ein Compiler-Flag der die => Initialisierung als Error melden würde, so würde ich es vermutlich> nutzen.
Das sehe ich auch so.
Ich verwende {} überall seit Jahren, so dass es bei mir schon eher
anders herum ist, und ich über () bei einer Konstruktion/Initialisierung
stolpere.
> Auch als "Profi" halte ich den sofort ersichtlichen Unterschied zw.> Initialisierung und Zuweisung nicht für eine Banalität.
Auch das war ja der Hintergrund der Frage: sehe ich genauso. Und vor
allem auch in Richtung Anfänger.
Sven B. schrieb:> Entsprechend pragmatisch verwende ich Brace-Initializer eigentlich> hauptsächlich zur Initialisierung von Vektoren oder seltener Structs.
Ein chaotisches Mischen der verwendeten Form halte ich für eine ganz
schlechte Idee: entweder so herum oder so herum.
Wilhelm M. schrieb:> Ein chaotisches Mischen der verwendeten Form halte ich für eine ganz> schlechte Idee: entweder so herum oder so herum.
Ein chaotisches Mischen von for und while halte ich für eine ganz
schlechte Idee: entweder nur for oder nur while benutzen! ;)
Wilhelm M. schrieb:> Woran liegt das? Das die geschweiften Klammern so ungünstig auf der> deutschen Tastatur liegen?
Für mich gibts da keinen großen Unterschied zu den anderen Klammern.
Yalu X. schrieb:> Zu den vielen Initialisierungssyntaxen¹, von denen keine überall> verwendbar ist, kam mit der Brace-Initialization halt noch eine weitere> hinzu, die wieder nicht überall verwendbar ist und zudem für numerische> Datentypen wie int, double usw. auch noch extrem hässlich aussieht.
Das ist für mich der Grund gegen die {}. Für mich ist der einzige
Vorteil der {} das verbotene narrowing, dafür bringen sie viel mehr
Komplexität und Chaos in einen Themenbereich, der vorher schon zu
komplex und chaotisch war.
Ich gehe diesen Weg:
Vincent H. schrieb:> Timur Doumlers Mantra ist "() und = überall, {} wos nicht anders> geht".Wilhelm M. schrieb:> Das ist einer der vielen Punkte, wo ich mir ein "C++2" wünschen würde,> bei dem auf Kosten der Abwärtskompatibilität sämtliche Altlasten über> Bord geworfen werden und die guten Features von C++ so überarbeitet> werden, dass sie in sich konsistent und durchgängig verwendbar sind.> Damit entstünde eine neue, aber insbesondere für C++-Programmierer> schnell zu erlernende Sprache mit deutlich reduziertem Sprachumfang, der> dann sogar wieder Platz für sinnvolle Erweiterungen böte.
Ich bin dafür! Allerdings sollten wir vorher noch erproben, wie
brauchbar modules und constrains sind. Objekte sollten automatisch
immutable sein und es sollte nur noch explizite und keine automatischen
Konvertierungen (narrowing/casts) geben. Und jetzt träume ich weiter vom
Weltfrieden ;-)
Yalu X. schrieb:> Folgende Syntax verwende ich nicht (und brauche sie hoffentlich auch> nie):>> int n{5}; // stark gruselig
Für lokale Variablen im Code ist das absolut gruselig, aber für
member-initializer finde ich das wiederum sehr passend.
Oliver
Oliver S. schrieb:> Für lokale Variablen im Code ist das absolut gruselig, aber für> member-initializer finde ich das wiederum sehr passend.
Ich denke, dass für jemanden, die viel generischen Code schreibt,
letztlich die brace-Syntax die präferierte (uniform!) sein wird.
Wilhelm M. schrieb:> Allein in den Beispielen, die in diesem Forum gepostet werden. sieht man> es selten bis nie.> Woran liegt das
Die allerwenigsten Compiler sind so modern.
Hätte man C Nachfolger nicht C99, C++, C++11 und C++13 etc benannt
sondern
C, NextC, NewProgLang, FixCode, dann wäre gleich vollkommen eindeutig
gewesen, dass die nachfolgenden Dialekte die Sprache vollkommen
inkompatibel mit älteren Compilern machen. Niemand programmiert in 'D'.
Einfach weil es den falschen Namen trug. Nur wenn man eine neue Sprache
C nennt gibt es Trottel die meinen das neue wäre irgendwie nutzbar.
Eine brace-Initialisierung in einem Programm führt eben dazu, dass es
kein C/C++ Programm mehr ist und nicht mehr übersetzt werden kann,
sondern einen neuen Compiler erfordert, der meistens dann auf den
gewünschten Plattformen nicht verfügbar ist.
Wer reusable code schreiben will, nutzt so neue Features nicht. Die
bringen ja nichtmal einen Fortschritt.
int i{6};
ist nichts anderes als das überall funktionierende
int i=6;
Schwieriger wird es, wenn man ein Array statischer Objekte mit
parametrisierten Konstruktoren anlegen will, irgendwann braucht man das
brace.
Kluge Leute haben früher den code so geschrieben, dass er per defines
auf allen, auch älteren Compilern funktioniert, z.B. wegen
Parameterliste nach K&R vs. C80.
Heute denkt ja kein Schwein mehr so weit.
MaWin schrieb:> Kluge Leute haben früher den code so geschrieben, dass er per defines> auf allen, auch älteren Compilern funktioniert, z.B. wegen> Parameterliste nach K&R vs. C80.>> Heute denkt ja kein Schwein mehr so weit.
In dem Fall ist das auch gut so...
Oliver
Wilhelm M. schrieb:> Seitdem es die sog. uniform-initialization Syntax (aka> brace-initialization) gibt,
Wieder so ein useless crap feature dieser Müllsprache.
> verwende ich sie konsequent. Dies hilft m.E.> sehr, gerade auch einem Neuling den Unterschied zwischen Initialisierung> und Zuweisung klar zu machen:> int x{};
Oh Gott und dafür braucht das Programmiererkind jetzt noch ein weiteres
Paar Stützräder? Ist das echt dein ernst?
Sklavenvermittler schrieb:> Oh Gott und dafür braucht das Programmiererkind jetzt noch ein weiteres> Paar Stützräder? Ist das echt dein ernst?
Wie Wahrscheinlich ist es, dass du C++ Programmieren kannst?
Sklavenvermittler schrieb:> Wieder so ein useless crap feature dieser Müllsprache.
Da du solche Aussagen von dir gibts, schätze ich sie kleiner als 0.1%
ein.
mh schrieb:> Sklavenvermittler schrieb:>> Oh Gott und dafür braucht das Programmiererkind jetzt noch ein weiteres>> Paar Stützräder? Ist das echt dein ernst?> Wie Wahrscheinlich ist es, dass du C++ Programmieren kannst?
Sagen wir mal so ich habe es knapp 15 Jahre gemacht bis ich es nicht
mehr musste.
> Sklavenvermittler schrieb:>> Wieder so ein useless crap feature dieser Müllsprache.> Da du solche Aussagen von dir gibts, schätze ich sie kleiner als 0.1%> ein.
Ja scho recht Bub. Alles supertoll in C++.
MaWin schrieb:> Wer reusable code schreiben will, nutzt so neue Features nicht. Die> bringen ja nichtmal einen Fortschritt.>> int i{6};>> ist nichts anderes als das überall funktionierende>> int i=6;
Das stimmt so nicht: die erste Initialisierung ist Direct-Initialization
und die zweite ist eine Copy-Initialization. Bei einem int besteht der
Unterschied nur in der Vermeidung des narrowing, bei UDT könnte die
zweite Form gar nicht möglich sein. Für generischen Code macht es also
schon einen gravierenden Unterschied.