Wie initialisiere ich ein Member-Array mit einer initializer_list?
Lesezeit: 9 Minuten
Ich komme mit C++0x auf den neuesten Stand und teste Dinge mit g++ 4.6
Ich habe gerade den folgenden Code ausprobiert und dachte, er würde funktionieren, aber er lässt sich nicht kompilieren. Ich bekomme den Fehler:
incompatible types in assignment of ‘std::initializer_list<const int>’ to ‘const int [2]’
struct Foo
{
int const data[2];
Foo(std::initializer_list<int const>& ini)
: data(ini)
{}
};
Foo f = {1,3};
JohannesD
Sie können einen variadischen Vorlagenkonstruktor anstelle eines Initialisierungslistenkonstruktors verwenden:
struct foo {
int x[2];
template <typename... T>
foo(T... ts) : x{ts...} { // note the use of brace-init-list
}
};
int main() {
foo f1(1,2); // OK
foo f2{1,2}; // Also OK
foo f3(42); // OK; x[1] zero-initialized
foo f4(1,2,3); // Error: too many initializers
foo f5(3.14); // Error: narrowing conversion not allowed
foo f6("foo"); // Error: no conversion from const char* to int
}
BEARBEITEN: Wenn du ohne Beständigkeit leben kannst, Eine andere Möglichkeit wäre, die Initialisierung zu überspringen und das Array im Funktionskörper zu füllen:
struct foo {
int x[2]; // or std::array<int, 2> x;
foo(std::initializer_list<int> il) {
std::copy(il.begin(), il.end(), x);
// or std::copy(il.begin(), il.end(), x.begin());
// or x.fill(il.begin());
}
}
Auf diese Weise verlieren Sie jedoch die Kompilierzeitgrenzen, die die vorherige Lösung bietet.
Ich denke, er will, dass das Array ist const… die erste Lösung erfüllt dies, die zweite nicht.
– Kartoffelklatsche
5. April 11 um 11:09 Uhr
Ja, ich hoffe eher, dass der Compiler einige Optimierungen basierend auf der Konstanz der Daten vornimmt. Trotzdem habe ich für die erste Lösung gestimmt, da sie mir eher gefiel. Ihre Methode zwei ist die Problemumgehung, für die ich mich beim Posten dieses Problems entschieden habe, aber ich möchte diesen Weg lieber nicht gehen.
– swestrup
5. April 11 um 11:50 Uhr
@swestrup: const informiert normalerweise nicht über die Optimierung; Der Compiler stellt solche Dinge durch eine variable Lebensdaueranalyse dar. const ist eine Korrektheitsprüfung und hilft der Sprachunterstützung des Nur-Lese-Speichers.
– Kartoffelklatsche
5. April 11 um 15:17 Uhr
Der variadische Vorlagenkonstruktor könnte mit anderen Konstruktoren in Konflikt geraten, daher denke ich nicht, dass es eine allgemeine Lösung für das Problem des OP ist (obwohl dies in diesem speziellen Fall möglicherweise der Fall ist).
– gnzlbg
10. Oktober 14 um 12:27 Uhr
In Ihrem zweiten Beispiel die Linie std::copy(x, x+2, il.begin()); sollte sein std::copy(il.begin(), il.end(), x); weil wir x initialisieren wollen[2] von il, nicht wahr?
– plx
6. Januar 16 um 20:30 Uhr
Kartoffelklatsche
Soweit ich das beurteilen kann, sollte die Verwendung der Listeninitialisierung des Funktionsarguments des Konstruktors (8.5.4/1) legal sein und viele der oben genannten Probleme lösen. GCC 4.5.1 auf ideone.com stimmt jedoch nicht mit dem Konstruktor überein und lehnt ihn ab.
#include <array>
struct Foo
{
std::array< int, 2 > const data;
Foo(std::array<int, 2> const& ini) // parameter type specifies size = 2
: data( ini )
{}
};
Foo f( {1,3} ); // list-initialize function argument per 8.5.4/1
Wenn Sie wirklich darauf bestehen initializer_list, können Sie verwenden reinterpret_cast um das zugrunde liegende Array der zu drehen initializer_list in ein Array im C-Stil.
Ich wollte diesem eine positive und eine negative Stimme geben, aber sie ließen mich nicht.
– TonyK
5. April 11 um 10:48 Uhr
Oh ick. Ich bin entsetzt und beeindruckt zugleich. Ich hoffe WIRKLICH, dass es einen weniger hackishen Weg gibt, dies zu tun.
– swestrup
5. April 11 um 10:49 Uhr
@Tony: Ich mache dir keine Vorwürfe, aber man muss mutig sein, um sich die Mächte des Bösen zunutze zu machen … richtig?
– Kartoffelklatsche
5. April 11 um 10:50 Uhr
Die letztere Lösung sollte AFAICS tatsächlich funktionieren: Funktionsargumentübergabe ist ein direkter Initialisierungskontext; Listeninitialisierung kann bei Direktinitialisierung erfolgen; Listen-Initialisierung eines Aggregats bewirkt, dass Aggregat-Initialisierung (8.5.4/3) auftritt; und std::array ist von Natur aus ein Aggregat. Scheint ein Fehler in GCC 4.5 zu sein.
– JohannesD
5. April 11 um 11:39 Uhr
Ihre zweite Lösung gefällt mir eher, aber g++ 4.6 akzeptiert sie leider auch nicht.
Die richtige Syntax für die zweite Lösung von Potatoswatter lautet:
Foo f( {{1,3}} ); //two braces
ein bisschen hässlich, nicht im Einklang mit dem allgemeinen Sprachgebrauch
Ivan Aksamentov – Tropfen
Nur eine kleine Ergänzung zur großartigen Antwort von JohannesD.
Falls keine Argumente übergeben werden foo Konstruktor, Array wird standardmäßig initialisiert. Aber manchmal möchten Sie das zugrunde liegende Array nicht initialisiert lassen (vielleicht aus Leistungsgründen). Sie können den Standardkonstruktor nicht zusammen mit einem variadic-templated hinzufügen. Die Problemumgehung ist ein zusätzliches Argument für den Konstruktor mit Variadic-Vorlage, um ihn vom Konstruktor mit null Argumenten zu unterscheiden:
Drei Jahre sind schnell vergangen, und Compiler-Implementierer haben die Standardkonformität ihrer Produkte bis zu dem Niveau verbessert, auf dem der Standardkonstruktor in Gegenwart eines variadischen Konstruktors nicht mehr als mehrdeutig angesehen wird. In der Praxis brauchen wir also keine zusätzlichen T t Argument für den variadischen Konstruktor, um Konstruktoren eindeutig zu machen.
Beide
array2d() {}
und
array2d() = default;
lässt das Array nicht initialisiert, wenn das Objekt ohne Argumente konstruiert wird. Dieses Verhalten ist bei allen wichtigen Compilern konsistent. Vollständiges Beispiel (Rexester):
Das Entfernen des Standardkonstruktors führt immer noch dazu, dass der variadische Konstruktor aufgerufen und das Array standardmäßig initialisiert wird (in unserem Fall nur mit Nullen).
Danke @Alek, dass du diesen Thread gepusht hast und auf diese Fakten aufmerksam gemacht hast, und auch danke an alle Leute, die hart an der Compiler-Entwicklung arbeiten.
Bo Persson
Sie können nicht, Arrays sind nicht wie andere Typen (und haben keine Konstruktoren, die eine std::initializer_list verwenden).
Ich denke, es würde auch mit std::array funktionieren, um der ursprünglichen Implementierung des OPs näher zu kommen.
– ronag
5. April 11 um 9:14 Uhr
@ronag: Ich glaube nicht, dass std::array überhaupt Konstruktoren hat, es soll wie ein Array im C-Stil initialisiert werden.
– Bo Persson
5. April 11 um 9:27 Uhr
Leider ist meine beabsichtigte Verwendung in einer Situation, in der der Overhead von std::vector nicht akzeptabel ist. std::array wäre in Ordnung, aber es hat genau das gleiche Problem.
– swestrup
5. April 11 um 9:35 Uhr
Das C++0x std::array sollte wirklich einen Initialisierungslistenkonstruktor sowie einen haben [begin, end) one. The reasons the boost/tr1 implementations do not, stem from C++03 limitations that do not exist in C++0x anymore.
– JohannesD
Apr 5 ’11 at 10:26
@Johannes: std::tuple, too. It makes me sad.
– Lightness Races in Orbit
Apr 5 ’11 at 19:29
Erik Schnetter
You can define a constexpr function that converts an initializer list to an array. The last (third) function is the one you call. The other create recursively a template parameter pack from the initializer list, and create the array once sufficiently many list elements have been read.
I think it would also work with std::array to get closer to the OPs original implementation.
– ronag
Apr 5 ’11 at 9:14
@ronag: I don’t think std::array has any constructors at all, it is supposed to be initialized just like a C style array.
– Bo Persson
Apr 5 ’11 at 9:27
Alas my intended use is in a situation where the overhead of std::vector is unacceptable. std::array would be fine, but it has the exact same problem.
– swestrup
Apr 5 ’11 at 9:35
The C++0x std::array really ought to have an initializer list constructor, as well as a [begin, end) one. The reasons the boost/tr1 implementations do not, stem from C++03 limitations that do not exist in C++0x anymore.
– JohannesD
Apr 5 ’11 at 10:26
@Johannes: std::tuple, too. It makes me sad.
– Lightness Races in Orbit
Apr 5 ’11 at 19:29
franjesus
While this does not work:
#include <initializer_list>
struct Foo
{
const int data[2]; constexpr Foo(const std::initializer_list& ini): data{ini} {} }; Foo f = {1,3};
Ich fand diesen einfachen Ansatz gut zu funktionieren:
struct Foo
{
const int data[2];
constexpr Foo(const int a, const int b): data{a,b} {}
};
Foo f = {1,3};
Natürlich ist der variadische Template-Ansatz wahrscheinlich besser, wenn Sie viele Elemente haben, aber in diesem einfachen Fall wird dies wahrscheinlich ausreichen.
Das heißt, wenn Sie den Konstruktor explizit aus Initialisierungslisten definieren möchten. Für die meisten POD-Fälle ist dies in Ordnung und gut:
struct Foo
{
const int data[2];
};
Foo f = {1,3};
.
7840500cookie-checkWie initialisiere ich ein Member-Array mit einer initializer_list?yes