Forum: PC-Programmierung Brace (Uniform) Initialization in C++


von Wilhelm M. (wimalopaan)


Lesenswert?

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
int x{};
2
double y{1.0};
3
A z{42];
4
5
x = 1;
6
y = 2.0;
7
8
for(auto i{size()}; i-- > 0;) {
9
}
10
11
std::array<int,3> a {1, 2, 3};
12
13
struct AG {
14
    double m1{};
15
    double m2{};
16
};
17
18
AG ag{1.0, 2.0};
19
20
struct X {
21
    explicit X(int v) : m{v} {}
22
    int m{};
23
    inline static const int m2{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?

: Verschoben durch Moderator
von Oliver S. (oliverso)


Lesenswert?

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

: Bearbeitet durch User
von Vincent H. (vinci)


Lesenswert?

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.

von Guest (Gast)


Lesenswert?

Ich verwende die "Uniform" Initialization Syntax nur wenn es nicht 
anders geht.
Denn es sind schon viel zu viele Curly Braces in meinem Code ;-)

von Oliver S. (oliverso)


Lesenswert?

Wilhelm M. schrieb:
> konsequent

Auch hier?
1
for (int i{0}; ...

Oliver

von Wilhelm M. (wimalopaan)


Lesenswert?

Oliver S. schrieb:
> Auch hier?
> for (int i{0}; ...

Steht doch oben ... gelesen?


In dem Fall
1
for(int i{}; ; ) {
2
}

von zitter_ned_aso (Gast)


Lesenswert?

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.

von Yalu X. (yalu) (Moderator)


Lesenswert?

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
Foo foo = { 1, 2, 3, 4 };  // mit Gleichheitszeichen

Geht das nicht, weil bspw. der Konstruktor als explicit deklariert ist:

1
Foo foo( { 1, 2, 3, 4 } );  // mit Konstruktorargument in runden
2
                            // Klammern

Folgende Syntax verwende ich nicht (und brauche sie hoffentlich auch
nie):

1
Foo foo { 1, 2, 3, 4 };  // leicht gruselig

Für alle anderen Klassen bzw. Konstruktoren:

1
Bar bar(1, 'a');  // mit Konstruktorargumenten in runden Klammern

Für Datentypen, die keine Klassen sind (int, double usw.) verwende ich
bevorzugt

1
int n = 5;  // mit Gleichheitszeichen, ohne geschweifte Klammern

Bei der Initialisierung von Membervariablen geht das nicht, deswegen
dort:

1
struct C {
2
  C(): n(5) {}  // mit Pseudokonstruktorargument in runden Klammern
3
  int n;
4
};


Folgende Syntax verwende ich nicht (und brauche sie hoffentlich auch
nie):

1
int n{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++.

: Bearbeitet durch Moderator
von Sven B. (scummos)


Lesenswert?

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.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Sven B. (scummos)


Lesenswert?

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! ;)

von Schetiphist (Gast)


Lesenswert?

Yalu X. schrieb:
> ein "C++2" wünschen

Ein "C+=2" wünsch ich mir.

von mh (Gast)


Lesenswert?

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 ;-)

von Oliver S. (oliverso)


Lesenswert?

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

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

Ich sach mal vorsichtig, daß die auch nur dafür eingeführt wurde. 
Möglichkeiten gabs ja schon vorher mehr als genug.

Oliver

von Johann L. (gjlayde) Benutzerseite


Lesenswert?

Yalu X. schrieb:
> Foo foo { 1, 2, 3, 4 };  // leicht gruselig

Konsequent wäre

> Foo foo { {1}, {2}, {3}, {4} };

: Bearbeitet durch User
von MaWin (Gast)


Lesenswert?

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.

von Oliver S. (oliverso)


Lesenswert?

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

von Sklavenvermittler (Gast)


Lesenswert?

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?

von mh (Gast)


Lesenswert?

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.

von Sklavenvermittler (Gast)


Lesenswert?

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++.

von Wilhelm M. (wimalopaan)


Lesenswert?

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.

Beitrag #6101428 wurde vom Autor gelöscht.
Bitte melde dich an um einen Beitrag zu schreiben. Anmeldung ist kostenlos und dauert nur eine Minute.
Bestehender Account
Schon ein Account bei Google/GoogleMail? Keine Anmeldung erforderlich!
Mit Google-Account einloggen
Noch kein Account? Hier anmelden.