[C++11] Uniform initialization - doch nicht so uniform?
Verfasst: Fr Mär 02, 2012 11:01 am
Hallo!
Ich möchte nur kurz von meinen Erfahrungen berichten.
Im neuen Standard gibt es einige Änderungen, wie man Objekte und Basisklassen initialisiert.
Wozu ist das überhaupt notwendig?
Betrachtet folgenden Code:
Alle dieser vier Zeilen dienen der *Absicht* ein Objekt zu deklarieren und zu initialisieren. Diese sehen aber unterschiedlich aus, und bei Vektor muss man sogar Code für die initialisierung bereitstellen. Warum sage ich "Absicht"? ÜBERRASCHUNG!! Die erste Zeile, die zwar aussieht wie eine Variablendeklaration ist eine Funktionsdefinition!
Na gut,
da hat sich was getan in C++11:
Sieht doch hübsch aus, alles einheitlich, oder? Die erste Zeile ist jetzt übrigens tatsächlich eine Variablendeklaration.
Das ganze funktioniert sogar in Konstruktoren:
Und es verhindert die "Verschmälerung" (narrowing), Beispielsweise von integer und floating point datentypen:
Dabei ist es übrigens völlig egal, ob zwischen den geschweiften Klammern und dem Variablennamen ein "=" steht oder nicht.
Bis jetzt alles schön und gut. Ich bin ein großer Fan.
Es wird sogar empfohlen *alle* initialisierungen auf diese Syntax umzustellen.
Das habe ich gemacht, und siehe da: zwei Problemchen:
1) Referenzen!
Wieso auf einmal? Tja, hier wird aus {a} erstmal ein temporäres Objekt des Typs A erstellt, und mit a initialisiert. Dieses temp. Objekt wäre dann RValue und müsste mit einer nicht konstanten Referenz verknüpft werden. Das macht keinen Sinn: was ist eine Referenz auf ein Temporäres Objekt?
Seltsame Sache ist das, aber leider ist das so, also muss man hier weiterhin a_ref() schreiben.
2) Wenn der Konstruktor eine intializer_list akzeptieren würde!
Die folgenden zwei Zeilen haben nicht den gleichen Effekt!!
Wieso nicht? Nun, das betrifft zwei Konstruktoren des vector's:
Die erste Zeile ruft den ersten konstruktor auf, es wird ein vektor mit 100 Elementen erstellt.
Die zweite Zeile erstellt aber ein objekt vom Typ std::initializer_list<int>, mit einem Element, nämlich 100 -> der zweite Konstruktor wird aufgerufen.
Wäre der zweite konstruktor nicht vorhanden, wären die beiden aufrufe das gleiche, denn dann wäre die initializer_list kein gültiges Argument für den ersten konstruktor, somit kann mit dem 100 nur ein size_t gemeint sein.
Tja, das finde ich persönlich wirklich unschön aber auch damit muss ich Leben.
Nichtsdestotrotz!
Ich finde die Initialisierung ist ne coole Sache, und ich werde sie trotz der Probleme so weit wie möglich verwenden. Die zwei Problemstellen muss man sich halt merken.
Gehet hin und verkündet die frohe Botschaft!
ps.: Wenn jemand weitere Probleme findet, bitte bitte postet sie hier.
Wenn ich welche finde mache ich das selbstverständlich auch.
Ich möchte nur kurz von meinen Erfahrungen berichten.
Im neuen Standard gibt es einige Änderungen, wie man Objekte und Basisklassen initialisiert.
Wozu ist das überhaupt notwendig?
Betrachtet folgenden Code:
Code: Alles auswählen
rectangle w (origin(), extents());
complex<double> c (2.71828, 3.14159);
int a[] = {1, 2, 3, ,4};
vector v; for(int i;i<=4;++i) v.push_back(i);
Na gut,
da hat sich was getan in C++11:
Code: Alles auswählen
rectangle w {origin(), extents()};
complex<double> c { 2.71828, 3.14159 };
int a[] {1, 2, 3, ,4};
vector v {1, 2, 3, ,4};
Das ganze funktioniert sogar in Konstruktoren:
Code: Alles auswählen
struct ABase {
ABase(int x) : _x{x} { }
private:
int _x;
};
struct A : public ABase {
A (double f) : ABase{42}, _f{f}
private:
double _f;
};
Code: Alles auswählen
int x0= 7.3; // ok: aber daten gehen verloren!
int x1 {7.3}; // error: narrowing
Bis jetzt alles schön und gut. Ich bin ein großer Fan.
Es wird sogar empfohlen *alle* initialisierungen auf diese Syntax umzustellen.
Das habe ich gemacht, und siehe da: zwei Problemchen:
1) Referenzen!
Code: Alles auswählen
struct A{};
int main()
{
A a;
A& a_ref{a}; // BUMM!!
}
Seltsame Sache ist das, aber leider ist das so, also muss man hier weiterhin a_ref() schreiben.
2) Wenn der Konstruktor eine intializer_list akzeptieren würde!
Die folgenden zwei Zeilen haben nicht den gleichen Effekt!!
Code: Alles auswählen
std::vector<int> v(100);
std::vector<int> v{100};
Code: Alles auswählen
explicit vector(size_type n); // 1
vector(initializer_list<T>, const Allocator& = Allocator()); // 2
Die zweite Zeile erstellt aber ein objekt vom Typ std::initializer_list<int>, mit einem Element, nämlich 100 -> der zweite Konstruktor wird aufgerufen.
Wäre der zweite konstruktor nicht vorhanden, wären die beiden aufrufe das gleiche, denn dann wäre die initializer_list kein gültiges Argument für den ersten konstruktor, somit kann mit dem 100 nur ein size_t gemeint sein.
Tja, das finde ich persönlich wirklich unschön aber auch damit muss ich Leben.
Nichtsdestotrotz!
Ich finde die Initialisierung ist ne coole Sache, und ich werde sie trotz der Probleme so weit wie möglich verwenden. Die zwei Problemstellen muss man sich halt merken.
Gehet hin und verkündet die frohe Botschaft!
ps.: Wenn jemand weitere Probleme findet, bitte bitte postet sie hier.
Wenn ich welche finde mache ich das selbstverständlich auch.