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

Wie initialisiere ich ein Member Array mit einer initializer list
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


1644070267 448 Wie initialisiere ich ein Member Array mit einer initializer list
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.

Foo(std::initializer_list<int> ini) // pass without reference- or cv-qualification
: data( reinterpret_cast< std::array< int, 2 > const & >( * ini.begin() )

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

    – swestrup

    5. April 11 um 11:57 Uhr

Laut Diskussion Hier:

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

Wie initialisiere ich ein Member Array mit einer initializer list
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:

template<class T, size_t rows, size_t cols>
class array2d
{
    std::array<T, rows * cols> m_Data;
    public:

    array2d() {}

    template <typename T, typename... Types>
    array2d(T t, Types... ts) : m_Data{ { t, ts... } } {}
};

Jetzt können Sie das Objekt mit Klammern initialisieren oder nicht initialisieren:

array2d<int, 6, 8> arr = { 0, 1, 2, 3 };  // contains 0, 1, 2, 3, 0, 0, 0, ...
array2d<int, 6, 8> arr2;                  // contains garbage

Aktualisierung 31.07.2016

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

#include <array>
#include <iostream>

template<class T, size_t rows, size_t cols>
class array2d
{
  public:
    std::array<T, rows * cols> m_Data;

    array2d() = default;

    template <typename... Types>
    array2d(Types... ts) : m_Data{ { ts... } } {}
};

int main()
{
    array2d<int, 6, 8> arr_init = { 0, 1, 2, 3 };
    array2d<int, 6, 8> arr_default;


    std::cout << "Initialized: n";
    for(const auto& a : arr_init.m_Data)
        std::cout << a << " ";
    std::cout << "n";

    std::cout << "Default: n";
    for(const auto& a : arr_default.m_Data)    
        std::cout << a << " ";

    std::cout << "n";
}

Ausgabe:

Initialized: 
0 1 2 3 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 
Default: 
2 0 -519559849 32558 1 32558 0 0 -519634912 32558 -526739248 32558 1 0 2 0 6295032 0 -519531243 32558 0 0 -1716075168 32765 6295648 0 4196192 0 6295648 0 -526527271 32558 1 0 2 0 6295032 0 4196845 0 124 0 0 0 4196768 0 4196518 0 

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.

1644070268 419 Wie initialisiere ich ein Member Array mit einer initializer list
Bo Persson

Sie können nicht, Arrays sind nicht wie andere Typen (und haben keine Konstruktoren, die eine std::initializer_list verwenden).

Versuchen Sie stattdessen Folgendes:

struct Foo  
{  
  const std::vector<int>   data;
  Foo(std::initializer_list<int> ini) : data(ini)
  {}
}; 

  • 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

Wie initialisiere ich ein Member Array mit einer initializer list
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.

template <typename T, size_t N, typename... Ts>
constexpr enable_if_t<(sizeof...(Ts) == N), array<T, N> >
array_from_initializer_list(const T *const beg, const T *const end,
                            const Ts... xs) {
  return array<T, N>{xs...};
}

template <typename T, size_t N, typename... Ts>
constexpr enable_if_t<(sizeof...(Ts) < N), array<T, N> >
array_from_initializer_list(const T *const beg, const T *const end,
                            const Ts... xs) {
  return array_from_initializer_list<T, N>(beg + 1, end, *beg, xs...);
}

template <typename T, size_t N>
constexpr array<T, N> array_from_initializer_list(initializer_list<T> l) {
  return array_from_initializer_list<T, N>(l.begin(), l.end());
}

  • 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

1644070269 823 Wie initialisiere ich ein Member Array mit einer initializer list
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};

.

784050cookie-checkWie initialisiere ich ein Member-Array mit einer initializer_list?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy