Kann ich auf frühere Mitglieder einer Initialisiererliste verweisen?

Lesezeit: 5 Minuten

Benutzer-Avatar
Jonathan Mei

Angenommen, ich möchte mich an ein Mitglied von wenden initializer_list die ich schon definiert habe. Kann ich es schaffen?

Dieser Code wird kompiliert und gibt das Erwartete aus: “13 55 ” in Visual Studio und gccich würde nur gerne wissen, ob es legal ist:

const int foo[2] = {13, foo[0] + 42};

  • @NathanOliver Danke, ich stimme zu. Aber das ist eine ganz andere Frage. Seitenweise Sachen über Strukturen durchzulesen, um eine Antwort auf Arrays zu finden, ist nicht konstruktiv.

    – Jonathan Mei

    20. November 2015 um 15:35 Uhr

  • DR1343 scheint nicht weit genug zu gehen; Was benötigt wird, ist eine absolute Aussage, die für die Aggregatinitialisierung ein Initialisierer ist darf nicht ausgewertet werden, bevor die Initialisierung des vorherigen Elements abgeschlossen ist. Wie Shafik sagt, scheint es derzeit keine Formulierung zu geben, die verhindert, dass alle Elemente der Liste ausgewertet und die Ergebnisse dann auf das Aggregat angewendet werden

    – MM

    23. November 2015 um 2:40 Uhr

  • Dies ist eher eine geklammerte Init-Liste als eine initializer_listnicht wahr?

    – Baum mit Augen

    10. Juli 2018 um 13:01 Uhr

  • @BaummitAugen Ja ist es

    – NathanOliver

    10. Juli 2018 um 13:02 Uhr


  • @NathanOliver Wow … ich bin dumm. Danke für den Link.

    – Jonathan Mei

    10. Juli 2018 um 13:14 Uhr

Benutzer-Avatar
Shafik Yaghmur

Was wir hier also haben, ist die Aggregatinitialisierung, die im Abschnitt behandelt wird 8.5.1 des Entwurfs des C++-Standards und es heißt:

Ein Aggregat ist ein Array oder eine Klasse […]

und:

Wenn ein Aggregat durch eine Initialisiererliste initialisiert wird, wie in 8.5.4 spezifiziert, werden die Elemente der Initialisiererliste als Initialisierer für die Mitglieder des Aggregats genommen, in aufsteigender Index- oder Mitgliederreihenfolge. Jeder Member wird von der entsprechenden Initialisierungsklausel kopiert […]

Obwohl es vernünftig erscheint, dass Nebenwirkungen aus der Initialisierung jedes Members des Aggregats vor dem nächsten sequenziert werden sollten, da jedes Element in der Initialisiererliste ein vollständiger Ausdruck ist. Der Standard garantiert dies nicht wirklich, daran können wir erkennen Mängelbericht 1343 was sagt:

Der aktuelle Wortlaut zeigt nicht an, dass die Initialisierung eines Nicht-Klassen-Objekts ein vollständiger Ausdruck ist, sollte dies aber vermutlich tun.

und auch anmerkungen:

Die Aggregatinitialisierung könnte auch mehr als einen vollständigen Ausdruck umfassen, daher ist die obige Einschränkung auf „Initialisierung eines Nicht-Klassenobjekts“ nicht korrekt.

und wir können von einem verwandten sehen std-Diskussionsthema Richard Smith sagt:

[intro.execution]p10: „Ein Vollausdruck ist ein Ausdruck, der kein Teilausdruck eines anderen Ausdrucks ist. […] Wenn ein Sprachkonstrukt definiert ist, um einen impliziten Aufruf einer Funktion zu erzeugen, wird eine Verwendung des Sprachkonstrukts als Ausdruck im Sinne dieser Definition angesehen.”

Da eine geklammerte Init-Liste kein Ausdruck ist und in diesem Fall nicht zu einem Funktionsaufruf führt, sind 5 und si getrennte Vollausdrücke. Dann:

[intro.execution]p14: „Jede Wertberechnung und Nebenwirkung, die einem Vollausdruck zugeordnet ist, wird vor jeder Wertberechnung und Nebenwirkung sequenziert, die dem nächsten zu bewertenden Vollausdruck zugeordnet ist.“

Die einzige Frage ist also, ob der Nebeneffekt der Initialisierung von si mit der Auswertung des vollständigen Ausdrucks „5“ „verbunden“ ist? Ich denke, die einzig vernünftige Annahme ist, dass es so ist: Wenn 5 ein Mitglied des Klassentyps initialisieren würde, wäre der Konstruktoraufruf offensichtlich Teil des vollständigen Ausdrucks durch die Definition in [intro.execution]p10, daher ist es naheliegend anzunehmen, dass dasselbe für skalare Typen gilt.

Ich glaube jedoch nicht, dass der Standard dies tatsächlich irgendwo ausdrücklich sagt.

Daher ist dies derzeit nicht durch den Standard spezifiziert und kann sich nicht darauf verlassen, obwohl ich mich wundern würde, wenn eine Implementierung es nicht so behandeln würde, wie Sie es erwarten.

Für einen einfachen Fall wie diesen scheint etwas Ähnliches eine bessere Alternative zu sein:

constexpr int value = 13 ;
const int foo[2] = {value, value+42};

Änderungen in C++17

Das Vorschlag P0507R0: Kernproblem 1343: Sequenzierung der Nicht-Klassen-Initialisierung verdeutlicht den angesprochenen Vollausdruckspunkt hier beantwortet aber nicht die Frage, ob der Nebeneffekt der Initialisierung in die Auswertung des vollständigen Ausdrucks einbezogen wird. Es ändert also nichts daran, dass dies unspezifiziert ist.

Die relevanten Änderungen für diese Frage sind in [intro.execution]:

Ein Konstituentenausdruck ist wie folgt definiert:

(9.1) — Der konstituierende Ausdruck eines Ausdrucks ist dieser Ausdruck.

(9.2) — Die konstituierenden Ausdrücke einer geklammerten Init-Liste oder einer (ggf. eingeklammerten) Ausdrucksliste sind die konstituierenden Ausdrücke der Elemente der jeweiligen Liste.

(9.3) — Die konstituierenden Ausdrücke eines Klammer-oder-Gleich-Initialisierers der form = initializer-clause sind die konstituierenden Ausdrücke der initializer-clause.
[ Example:

struct A { int x; };
struct B { int y; struct A a; };
B b = { 5, { 1+1 } };

The constituent expressions of the initializer used for the initialization of b are 5 and 1+1. —end example ]

und [intro.execution]p12:

Ein Vollausdruck ist

(12.1) — ein unbewerteter Operand (Abschnitt 8),

(12.2) — ein konstanter Ausdruck (8.20),

(12.3) — ein Init-Declarator (Abschnitt 11) oder ein Mem-Initialisierer (15.6.2), einschließlich der konstituierenden Ausdrücke des Initialisierers,

(12.4) – ein Aufruf eines Destruktors, der am Ende der Lebensdauer eines anderen Objekts als eines temporären Objekts (15.2) generiert wird, oder

(12.5) — ein Ausdruck, der kein Unterausdruck eines anderen Ausdrucks ist und der ansonsten nicht Teil eines vollständigen Ausdrucks ist.

Also in diesem Fall beides 13 und foo[0] + 42 sind konstituierender Ausdruck die Teil einer sind Vollausdruck. Dies ist eine Pause von der Analyse hier die postulierten, dass sie jeweils ihre eigenen vollständigen Ausdrücke sein würden.

Änderungen in C++20

Das Designierter Initialisierungsvorschlag: P0329 enthält den folgenden Zusatz, der dies gut definiert zu machen scheint:

11.6.1 einen neuen Absatz hinzufügen [dcl.init.aggr]:

Die Initialisierungen der Elemente des Aggregats werden in der Reihenfolge der Elemente ausgewertet. Das heißt, alle Wertberechnungen und Seiteneffekte, die einem gegebenen Element zugeordnet sind, werden vor denen aller Elemente sequenziert, die ihm der Reihe nach folgen.

Wir können sehen, dass sich dies in der widerspiegelt neuster Normentwurf.

  • Wie wäre es mit eel.is/c++draft/dcl.init.aggr#6? Da steht doch ziemlich explizit, dass es erlaubt ist.

    – Rakete1111

    10. Juli 2018 um 13:08 Uhr

  • @ Rakete1111 Dies wurde erst kürzlich hinzugefügt IIRC, wird aktualisiert, wenn ich die Möglichkeit habe, es erneut nachzuschlagen.

    – Shafik Yaghmour

    10. Juli 2018 um 13:11 Uhr

  • Oh, du hast Recht, es wurde mit ausgewiesenen Initialisierungslisten hinzugefügt 🙂 Danke.

    – Rakete1111

    10. Juli 2018 um 13:15 Uhr

  • @Rakete1111 Habe ich neulich gemacht eine Twitter-Umfrage zu einem ähnlichen und diese Frage ist eigentlich dieser ähnlich

    – Shafik Yaghmour

    10. Juli 2018 um 17:09 Uhr

  • 13 und foo[0] + 42 gehören ein Vollausdruck. Warum also wird der Nebeneffekt eines Initialisierers vor dem des anderen sequenziert?

    – xskxzr

    16. Juli 2018 um 14:26 Uhr


1012930cookie-checkKann ich auf frühere Mitglieder einer Initialisiererliste verweisen?

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

Privacy policy