[solved] std::array in initialisierungsliste initialisieren
-
Hallo,
wie kann ich in folgendem Beispiel die Objekte des Arrays korrekt initialisieren?
#include <array> #include <string> using namespace std; class elem { public: elem(string s) : str(s) { } private: elem(const elem&) { } string str; }; class master { public: master() : elements{{"foo", "bar", "baz"}} // error: could not convert const char* to elem // dies tritt bei verschiedenen Arten der Klammersetzung auf // (die habe ich ehrlich gesagt auch noch nicht so ganz kapiert) { } private: array<elem, 3> elements; }; int main() { }
const char* ist doch implizit in std::string konvertierbar? Warum funktioniert das nicht?
Wenn ich elem einen Konstruktor verpasse, der einen const char* nimmt, ist alles gut, aber das geht in meiner realen Anwendung nicht, da der Kopierkonstruktor von elem private ist. Das kann ich auch nicht aendern (die Klasse erbt von einer Klasse einer Third-Party-Library).
Wie kann ich das (auf einfache Weise) loesen?
-
Du hast hier 2 implizite Konvertierungen. Einmal von const char* zu std::string und dann von std::string zu elem. Das geht nicht.
Die strings in std::string() zu verpacken oder bei C++11/14 (weiß nicht mehr so genau) ein s hinter das Anführungszeichen zu setzen, sollte helfen.
-
Ja, das habe ich mir auch gedacht, ergibt ja auch Sinn. Da aber Copy-Konstruktor von elem privat ist, erhalte ich:
main.cpp:15:9: error: ‘elem::elem(const elem&)’ is private elem(const elem&) { } ^ main.cpp:23:58: error: within this context master() : elements{{string("foo"), "bar", "baz"}}
Ich habe das vorherige Beispiel angepasst, damit die Zeilennummer passt (sollte hier ja eh klar sein, aber so ists konsistent
)
-
Ok, whatever. Wenn man mit Klammern um sich schmeisst, ist das Beispiel kompilierbar. Erstmal geloest, das ist gut, ich wuerde mich aber freuen, wenn mir das jemand erklaeren koennte:
class master { public: master() : elements{{ {string("foo")}, {string("bar")}, {string("baz")} }} { } private: array<elem, 3> elements; };
-
Also von außen nach innen:
elements{...} - ist ein Aggregat, kein Konstruktor, geschweifte Klammern für Aggregatinitialisierung. In die geschweiften Klammern müssen alle Member initialisiert werden.
elements{{...}} - es hat ein Member, ein Array. Array kann man wiederum ebenfalls mit geschweiften Klammern initialisieren, dort kommen die einzelnen Elemente.
elements{{{...}, {...}, {...}}} - Initialisierung der einzelnen Elemente, wiederum mit geschweiften Klammern. Darein kommen dann die Konstruktor Argumente für elem (hier kannst du übrigens das string(...) weglassen.
-
Hm, ok, das klingt so halbwegs einleuchtend. Ich denke, ich muss mich da einfach nochmal genauer einlesen. Aggregat habe ich mal gehoert, kann aber nicht so viel damit anfangen. Trotzdem vielen Dank, auch schoen zu wissen, dass ich dann string() weglassen kann. Sieht dann doch schon viel schoener aus.
-
Aggregatinitialisierung bedeutest copy-initialisierung der Elemente. Damit sollte der Compiler eigentlich immer über den privaten Copy-ctor stolpern.
-
Ok, aber wenn ich das richtig sehe, werden durch die Klammern um die Elemente diese direkt ohne Kopie initialisiert. Richtig?
std::array<int, 3> arr1{{1, 2, 3}}; // ok, ints kann man kopieren std::array<non_copyable_and_ctor_with_string, 3>{{"foo", "bar", "baz"}}; // no... std::array<non_copyable_and_ctor_with_string, 3>{{{"foo"}, {"bar"}, {"baz"}}}; // yes! :)