¿Cómo es "int main () {(([] () {}) ())}} válido C ++?

271

Recientemente me encontré con el siguiente código esotérico.

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

Vuelva a formatearlo de la siguiente manera para que sea más legible:

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

Pero no puedo entender cómo (([](){})())es el código válido.

  • No se parece a la sintaxis del puntero de función.
  • No puede ser un truco de sobrecarga del operador. El código se compila tal cual.

Google no ayudó mucho con esta búsqueda de todos los símbolos. Pero se compila en Visual Studio 2010 y no genera nada. No hubo errores ni advertencias. Entonces parece un código válido.

Nunca he visto ningún código válido que es tan extraño fuera de Javascript y C punteros de función .

¿Alguien puede explicar cómo es válido C ++?

Místico
fuente
94
¡Oye! Eso es mio. "Don't sweat it. We have int main(){(([](){})());} which is valid C++" (9 de noviembre en el chat)
sehe
31
es un cierre lambda c ++ 11
77
@Mysticial: este código lo desconcierta porque es inútil. Si este lambda hiciera algo, reconocerías que tiene una sintaxis similar a los punteros de función (con los que está estrechamente relacionado).
SChepurin
14
@Mysticial - "6 años de C ++" - las lambdas se agregaron en C ++ 11, por lo que nadie tiene experiencia con ellas hace aproximadamente un año.
Pete Becker
50
La URL aquí es bastante divertida: "how-is-int-main-valid-c"
tckmn

Respuestas:

283

El código esencialmente llama una lambda vacía.

Comencemos desde el principio: [](){}es una expresión lambda vacía .

Luego, en C y C ++, puede ajustar expresiones en parens y se comportan exactamente igual como si estuvieran escritas sin ellas, así que eso es lo que hace el primer par de parens alrededor del lambda. Ahora estamos en ([](){}).

Luego, ()después del primer ajuste, parens llama a la lambda (vacía). Ahora estamos en([](){})()

Toda la expresión está envuelta en parens de nuevo y la obtenemos (([](){})()).

Por fin, ;termina la declaración. Llegamos a (([](){})());.


† Hay algunos casos de esquina al menos en C ++, como con T a_var; una diferencia entre decltype(a_var)ydecltype((a_var)) .

Xeo
fuente
77
Perdí una daga.
R. Martinho Fernandes
33
@ R.MartinhoFernandes: Todavía estaba atrapado en alguien, así que tuve que ir y recuperarlo.
Xeo
1
Iba a votar por mencionar correctamente el caso en el que agregar () alrededor de una expresión altera la semántica. Pero luego recordé que realmente no tiene relación con la pregunta. Buena respuesta
sehe
2
Los paréntesis también están cambiando el significado de un programa en el caso de la desambiguación de análisis más irritante: B foo(A())foo es una función (tomar un puntero para que funcione como único parámetro y devolver un B) mientras que en B foo((A()))foo es un objeto B construido invocando a un constructor a Un objeto (cuya instancia es temporal anónima en este caso).
Anuncio N
1
@AdN: Eso ya no es una expresión, sino una declaración.
Xeo