Wie ist "int main () {(([] () {}) ());}" in C ++ gültig?

271

Ich bin kürzlich auf den folgenden esoterischen Code gestoßen.

int main(){(([](){})());}

Formatieren Sie es wie folgt neu, um es besser lesbar zu machen:

int main(){
    (([](){})());   //  Um... what?!?!
}

Aber ich kann mir nicht vorstellen, wie (([](){})())gültiger Code ist.

  • Es sieht nicht nach Funktionszeigersyntax aus.
  • Es kann kein Operator-Überladetrick sein. Der Code wird unverändert kompiliert.

Google hat bei dieser Suche mit allen Symbolen nicht viel geholfen. Es wird jedoch in Visual Studio 2010 kompiliert und gibt nichts aus. Es gab keine Fehler und keine Warnungen. Es sieht also nach gültigem Code aus.

Ich habe noch nie einen gültigen Code gesehen, der außerhalb von Javascript- und C-Funktionszeigern so bizarr ist .

Kann jemand erklären, wie dies in C ++ gültig ist?

Mystisch
quelle
94
Hallo! Das ist meins. "Don't sweat it. We have int main(){(([](){})());} which is valid C++" (9. November im Chat)
sehe
31
Es ist ein C ++ 11 Lambda-Verschluss
7
@Mysticial - Dieser Code verwirrt Sie, weil er nutzlos ist. Wenn dieses Lambda etwas tun würde, würden Sie erkennen, dass es eine ähnliche Syntax wie Funktionszeiger hat (mit denen es eng verwandt ist).
SChepurin
14
@Mysticial - "6 Jahre C ++" - Lambdas wurden gerade in C ++ 11 hinzugefügt, sodass vor etwa einem Jahr niemand Erfahrung mit ihnen hat.
Pete Becker
50
Die URL hier ist ziemlich amüsant: "wie-ist-int-main-gültig-c"
tckmn

Antworten:

283

Der Code nennt im Wesentlichen ein leeres Lambda.

Beginnen wir von vorne: [](){}ist ein leerer Lambda-Ausdruck .

Dann können Sie in C und C ++ Ausdrücke in Parens einschließen, und sie verhalten sich genau so †, als ob sie ohne sie geschrieben worden wären. Das ist also, was das erste Paar von Parens um das Lambda tut. Wir sind jetzt bei ([](){}).

Dann ()ruft parens nach dem ersten Wickeln das (leere) Lambda auf. Wir sind jetzt bei([](){})()

Der ganze Ausdruck ist wieder in Parens gewickelt und wir bekommen (([](){})()).

Endlich ;endet die Aussage. Wir kommen an (([](){})());.


† Zumindest in C ++ gibt es einige Eckfälle, zum Beispiel T a_var; gibt es einen Unterschied zwischen decltype(a_var)unddecltype((a_var)) .

Xeo
quelle
7
Verpasste einen Dolch.
R. Martinho Fernandes
33
@ R.MartinhoFernandes: Es steckte immer noch in jemandem fest, also musste ich es abrufen.
Xeo
1
Ich wollte dafür stimmen , dass ich den Fall richtig erwähnt habe, in dem das Hinzufügen () um einen Ausdruck die Semantik verändert. Aber dann erinnerte ich mich, dass das eigentlich nichts mit der Frage zu tun hat. Schöne Antwort
sehe
2
Die Klammern ändern auch die Bedeutung eines Programms im Fall der ärgerlichsten Analyse-Disambiguierung: B foo(A())foo ist eine Funktion (einen Zeiger auf die Funktion als einzigen Parameter nehmen und ein B zurückgeben), während in B foo((A()))foo ein B-Objekt konstruiert ist, das einen Konstruktor aufruft a Ein Objekt (welche Instanz ist in diesem Fall ein anonymes temporäres Objekt).
Ad N
1
@AdN: Das ist kein Ausdruck mehr, sondern eine Erklärung.
Xeo