Warum leitet auto x {3} eine initializer_list ab?

Ich liebe auto in C ++ 11. Es ist wunderbar. Aber es hat eine Inkonsistenz, die mir wirklich auf die Nerven geht, weil ich ständig stolpere:

 int i = 3; // i is an int with value 3 int i = int{3}; // i is an int with value 3 int i(3); // i is an int with value 3 (possibly narrowing, not in this case) int i{3}; // i is an int with value 3 auto i = 3; // i is an int with value 3 auto i = int{3}; // i is an int with value 3 auto i(3); // i is an int with value 3 auto i{3}; // wtf, i is a std::initializer_list?! 

Dieses seltsame Verhalten ist für Neulinge verwirrend und für erfahrene Benutzer lästig – C ++ hat genug kleine Inkonsistenzen und Eckenfälle, die man beachten sollte, so wie es ist. Kann jemand erklären, warum der Normenausschuss beschlossen hat, in diesem Fall einen neuen einzuführen?

Ich konnte es verstehen, wenn eine Variable vom Typ std::initializer_list etwas war, das nützlich oder häufig war, aber nach meiner Erfahrung ist es fast nie beabsichtigt – und in den seltenen Fällen, in denen Sie es tun wollten

 std::initializer_list l{3}; auto l = std::initializer_list{3}; auto l = {3}; // No need to specify the type 

würde gut funktionieren. Was ist der Grund für den Sonderfall für auto x{i} ?

Um es kurz zu machen:

  • Ein formatierter Initialisierungsausdruck {} hat keinen eigenen Typ
  • auto muss auf Typinformationen schließen
  • int{3} bedeutet offensichtlich “erstelle eine int var mit dem Wert aus der Initialisiererliste”, daher ist sein Typ nur int und kann in jedem weiteren Kontext verwendet werden ( int i = int{3} wird funktionieren und auto i = int{3} kann Typ ableiten, da die rechte Seite offensichtlich vom Typ int )
  • {3} selbst hat keinen Typ (es kann nicht int , weil es kein Wert ist, sondern eine Initialisierungsliste ), also würde auto nicht funktionieren – aber weil das Komitee dachte, dass auto in diesem Fall immer noch funktionieren sollte, entschieden sie sich dass der “beste” Typ für (yeah, typeless per definition) Initialisierungsliste wäre … std::initializer_list , wie Sie wahrscheinlich schon erraten haben.

Aber, wie Sie darauf hingewiesen haben, machte dies das ganze Verhalten von auto ziemlich semantisch inkonsistent. Deshalb wurden dem Ausschuss Vorschläge zur Änderung unterbreitet – nämlich N3681 , N3912 und N3922 . Ehemaliger Vorschlag wurde als FI3 abgelehnt, da in dieser Frage kein Komitee-Konsens bestand, http://isocpp.org/files/papers/n3852.html#FI3 , aktuell ( N3922 ) wurde ca. 1. Quartal 2015 ;

tl; dr Sie können davon ausgehen, dass standardkonforme Compiler 1 mit modernster C ++ – Unterstützung 2 entweder die neue, vernünftigere Semantik bereits installiert haben oder in Kürze haben werden.

Der Standardisierungsausschuss erkannte das Problem an, indem er N3922 in den Entwurf C ++ 17 übernommen hat.

– so ist es

 auto x1 = { 1, 2 }; // decltype(x1) is std::initializer_list auto x2 = { 1, 2.0 }; // error: cannot deduce element type auto x3{ 1, 2 }; // error: not a single element auto x4 = { 3 }; // decltype(x4) is std::initializer_list auto x5{ 3 }; // decltype(x5) is int 

jetzt, zum Guten oder zum Schlechten.

weitere Lektüre:

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2013/n3681.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3912.html

http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2014/n3922.html

http://scottmeyers.blogspot.com/2014/03/if-brazed-initializers-have-no-type-why.html

http://herbsutter.com/2014/11/24/updates-to-my-trip-report/


1 GCC 5.1 (& up) verwendet offenbar N3922 sogar im C ++ 11 / C ++ 14-Modus

2 Clang 3.8, mit dem Vorbehalt

Dies ist eine rückwärtskompatible Änderung, die auf alle Sprachversionen angewendet wird, die den Typabzug von auto zulassen (auf Anfrage des C ++ – Komitees).