W jaki sposób „int main () {(([] () {}) ());}” jest poprawnym C ++?

271

Ostatnio natknąłem się na następujący ezoteryczny fragment kodu.

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

Sformatuj go w następujący sposób, aby był bardziej czytelny:

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

Ale nie mogę się zastanowić, jak (([](){})())ważny jest kod.

  • Nie wygląda na składnię wskaźnika funkcji.
  • To nie może być jakiś sposób na przeciążenie operatora. Kod kompiluje się w obecnej postaci.

Google niewiele pomogło w tym wyszukiwaniu wszystkich symboli. Ale kompiluje się w Visual Studio 2010 i nic nie wyświetla. Nie było błędów ani ostrzeżeń. Wygląda to na prawidłowy kod.

Nigdy nie widziałem żadnego ważnego kodu, który jest tak dziwaczna poza Javascript i C wskaźników funkcji .

Czy ktoś może wyjaśnić, jak to jest poprawne C ++?

Tajemniczy
źródło
94
Hej! To moje. Don't sweat it. We have int main(){(([](){})());} which is valid C++" (9 listopada na czacie)
patrz
31
jest to zamknięcie lambda c ++ 11
7
@Mysticial - Ten kod Cię zaskakuje, ponieważ jest bezużyteczny. Jeśli ta lambda coś zrobi, rozpoznasz, że ma ona składnię podobną do wskaźników funkcji (z którymi jest ściśle powiązana).
SChepurin,
14
@Mysticial - „6 lat C ++” - lambdy zostały właśnie dodane w C ++ 11, więc nikt nie miał z nimi doświadczenia przed rokiem.
Pete Becker
50
Adres URL jest tutaj dość zabawny: „how-is-int-main-valid-c”
tckmn

Odpowiedzi:

283

Kod zasadniczo nazywa pustą lambda.

Zacznijmy od początku: [](){}jest pustym wyrażeniem lambda .

Następnie w C i C ++ możesz zawijać wyrażenia w pareny i zachowują się dokładnie tak samo jak gdyby zostały napisane bez nich, więc to właśnie robi pierwsza para parenów wokół lambdy. Jesteśmy teraz na ([](){}).

Następnie, ()po pierwszym zawijaniu parens wywołuje (pustą) lambda. Jesteśmy teraz na([](){})()

Całe wyrażenie jest ponownie owinięte w pareny i otrzymujemy (([](){})()) .

W końcu ;kończy się wypowiedź. Dojeżdżamy do (([](){})());.


† Istnieją pewne przypadki narożne przynajmniej w C ++, na przykład z T a_var; różnicą między decltype(a_var)idecltype((a_var)) .

Xeo
źródło
7
Brakowało sztyletu.
R. Martinho Fernandes
33
@ R.MartinhoFernandes: Wciąż był w kimś uwięziony, więc musiałem go znaleźć.
Xeo
1
Chciałem głosować za poprawnym wspomnieniem przypadku, w którym dodanie () wokół wyrażenia zmienia semantykę. Ale potem przypomniałem sobie, że tak naprawdę nie ma to związku z pytaniem.
Dobra
2
Nawiasy zmieniają również znaczenie programu w przypadku najbardziej irytującego ujednoznacznienia analizy składni: B foo(A())foo jest funkcją (przyjmowanie wskaźnika do funkcji jako jedynego parametru i zwracanie B), podczas gdy w B foo((A()))foo jest to obiekt B skonstruowany przy użyciu konstruktora obiekt A (która instancja jest w tym przypadku anonimową tymczasową).
Ogłoszenie
1
@AdN: To już nie jest wyrażenie, ale deklaracja.
Xeo,