Initialisierungslisten und RHS von Operatoren

Lesezeit: 5 Minuten

Initialisierungslisten und RHS von Operatoren
mavam

Ich verstehe nicht, warum Initialisierungslisten nicht auf der RHS eines Operators verwendet werden können. Erwägen:

class foo { };

struct bar
{
    template<typename... T>
    bar(T const&...) { }
};

foo& operator<<(foo& f, bar const&) { return f; }

int main()
{
    foo baz;
    baz << {1, -2, "foo", 4, 5};

    return 0;
}

Der neueste Clang (ebenfalls gcc) beschwert sich:

clang.cc:14:9: error: initializer list cannot be used on the right hand side of operator '<<'
    baz << {1, -2, "foo", 4, 5};
    ^  ~~~~~~~~~~~~~~~~~~~~

    ^  ~~~~~~~~~~~~~~~

Warum sollte der C++-Standard dies verbieten? Oder anders ausgedrückt, warum scheitert dies im Gegensatz zu

baz << bar{1, -2, "foo", 4, 5};

?

  • Weil du nicht überladen hast operator<< ein nehmen initializer_list<> auf der RHS… Was ist Ihre eigentliche Frage?

    – Ildjarn

    10. Juli 2012 um 19:28 Uhr


  • Ich hatte gehofft, dass dies gleichwertig ist baz << bar{1, 2, 3, 4, 5};aber anscheinend findet keine Konvertierung statt.

    – mavam

    10. Juli 2012 um 19:34 Uhr

  • Wenn das das gewünschte Verhalten ist, sollten Sie vielleicht versuchen, etwas zu geben bar ein nicht expliziter Konstruktor, der eine einzelne akzeptiert initializer_list<>.

    – Ildjarn

    10. Juli 2012 um 19:35 Uhr


  • Das kann nicht funktionieren, weil ein initializer_list hat exakt ein Vorlagenparameter, im Gegensatz zum variadischen Konstruktor mit mehreren Argumenttypen.

    – mavam

    10. Juli 2012 um 19:44 Uhr

  • Es ist lustig, wie das: operator<<(baz, {1, -2, "foo", 4, 5}); funktioniert.

    – mfontanini

    10. Juli 2012 um 20:23 Uhr

1646906412 321 Initialisierungslisten und RHS von Operatoren
Jogojapan

Tatsächlich ermöglicht die endgültige Version von C++11 nicht die Verwendung von Initialisierungslisten auf der rechten Seite (oder in diesem Fall auf der linken Seite) eines binären Operators.

Zuerst, Initialisierungslisten sind keine Ausdrücke wie in §5 des Standards definiert. Die Argumente von Funktionen sowie von binären Operatoren müssen im Allgemeinen Ausdrücke sein, und die in §5 definierte Grammatik für Ausdrücke enthält nicht die Syntax für Klammer-Init-Listen (dh reine Initialisierungslisten; beachten Sie, dass ein Typname gefolgt von eine Klammer-Init-Liste, wie z bar {2,5,"hello",7} ist aber ein Ausdruck).

Um reine Initialisierungslisten bequem nutzen zu können, definiert der Standard verschiedene Ausnahmen, die in folgendem (nicht normativen) Hinweis zusammengefasst sind:

§8.5.4/1
[…] Hinweis: Listeninitialisierung kann verwendet werden
— als Initialisierer in einer Variablendefinition (8.5)
— als Initialisierer in einem neuen Ausdruck (5.3.4)
— in einer Rückgabeerklärung (6.6.3)
— als Funktionsargument (5.2.2)
— als Index (5.2.1)
— als Argument für einen Konstruktoraufruf (8.5, 5.2.3)
— als Initialisierer für ein nicht statisches Datenelement (9.2)
— in einem Speicherinitialisierer (12.6.2)
— auf der rechten Seite einer Zuweisung (5.17)
[…]

Der vierte Punkt oben erlaubt ausdrücklich reine Initialisierungslisten als Funktionsargumente (weshalb operator<<(baz, {1, -2, "foo", 4, 5}); funktioniert), der fünfte erlaubt es in tiefgestellten Ausdrücken (dh als Argument von operator[]z.B mymap[{2,5,"hello"}] legal ist), und der letzte Punkt erlaubt ihnen auf der rechten Seite Zuordnungen (aber keine allgemeinen binären Operatoren).

Es gibt keine solche Ausnahme für binäre Operatoren wie +, * oder <<daher können Sie keine reine Initialisierungsliste (dh eine, der kein Typname vorangestellt ist) auf beiden Seiten von ihnen platzieren.

Was die angeht Gründe dafürein Entwurfs-/Diskussionspapier N2215 von Stroustrup und Dos Reis aus dem Jahr 2007 bietet viele Einblicke in viele der Probleme mit Initialisierungslisten in verschiedenen Kontexten. Insbesondere gibt es einen Abschnitt über binäre Operatoren (Abschnitt 6.2):

Betrachten Sie allgemeinere Verwendungen von Initialisiererlisten. Zum Beispiel:

v = v+{3,4};
v = {6,7}+v;

Wenn wir Operatoren als syntaktischen Zucker für Funktionen betrachten, betrachten wir natürlich das obige Äquivalent zu

v = operator+(v,{3,4});
v = operator+({6,7},v);

Es liegt daher nahe, die Verwendung von Initialisiererlisten auf Ausdrücke auszudehnen. Es gibt viele Anwendungen, bei denen Initialisierungslisten in Kombination mit Operatoren eine „natürliche“ Notation sind.
Es ist jedoch nicht trivial, eine LR(1)-Grammatik zu schreiben, die eine beliebige Verwendung von Initialisiererlisten erlaubt. Ein Block beginnt auch mit einem {, so dass das Zulassen einer Initialisierungsliste als erste (ganz links) Entität eines Ausdrucks zu Chaos in der Grammatik führen würde.
Es ist trivial, Initialisiererlisten als rechten Operanden von binären Operatoren, in Indizes und ähnlichen isolierten Teilen der Grammatik zuzulassen. Das eigentliche Problem ist zuzulassen ;a={1,2}+b; als Zuweisungsangabe, ohne auch zu erlauben ;{1,2}+b;. Wir vermuten, dass Initialisierungslisten als rechte Hand zulässig sind, aber nicht [sic] da Argumente für die linke Hand für die meisten Operatoren zu viel Klacks sind, […]

Mit anderen Worten, Initialisierungslisten sind auf der rechten Seite nicht aktiviert weil sie auf der linken Seite nicht aktiviert sindund sie sind auf der linken Seite nicht aktiviert, da dies eine zu große Herausforderung für Parser dargestellt hätte.

Ich frage mich, ob das Problem hätte vereinfacht werden können, indem ein anderes Symbol anstelle von geschweiften Klammern für die Syntax der Initialisiererliste ausgewählt wurde.

  • Ein anderes Symbol hätte vielleicht mehr Dinge möglich gemacht, aber {} ist eine solche natürliche Erweiterung der von C89 geerbten Array-Initialisierer und POD-Struktur-Initialisierer.

    – Aschepler

    13. Juli 2012 um 1:15 Uhr

  • Danke für diese Erklärung – ich habe nach Tenary-Operator gesucht true?{1,2,3}:{4,5,6} – das ist also nicht nur ein Problem mit binären Operatoren …

    – PiotrNycz

    13. Juni 2014 um 15:40 Uhr

  • @PiotrNycz Das ist möglicherweise kein Analyseproblem, sondern ein Typabzugsproblem. Beide Alternativen des ternären Operators müssen sich auf einen gemeinsamen Typ einigen, und etwas muss auch die Ergebniswertkategorie bestimmen. Die geschweiften Klammern verdecken die Bedeutung des Programms. Es ist besser drin ?: Um explizit zu sein, setzen Sie den Typnamen vor die {und das büßt keine Ausdruckskraft ein.

    – Kartoffelklatsche

    12. Juli 2014 um 0:32 Uhr

  • @jogojapan Glaubst du, diese Argumentation – RHS zu verbieten, weil wir nicht beides haben können, ist gerechtfertigt? Ist das nicht ein Fall „das Vollkommene auf dem Wege des Guten“? Immerhin ist es eine leistungsstarke Funktion, die nur für RHS nützlich ist. Zum Beispiel reicht für das OP-Beispiel RHS aus, das Vertauschen der Argumente macht nicht einmal Sinn!

    – Benutzer362515

    19. September 2016 um 17:54 Uhr

  • @jogojapan Ja, ich glaube nicht, dass nur RHS “ein Kludge” ist – dort ist ein Abzug viel häufiger erforderlich. Ich habe eine Diskussion in der Hoffnung begonnen, dass die drakonischen Regeln noch einmal aufgegriffen werden groups.google.com/a/isocpp.org/d/msg/std-proposals/nXjimf0amus/…

    – Benutzer362515

    20. September 2016 um 8:05 Uhr

987460cookie-checkInitialisierungslisten und RHS von Operatoren

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

Privacy policy