Una lambda positiva: ‘+ {}’ – ¿Qué hechicería es esto?

En la pregunta de desbordamiento de stack Redefinir lambdas no permitido en C ++ 11, ¿por qué? , se dio un pequeño progtwig que no comstack:

int main() { auto test = []{}; test = []{}; } 

La pregunta fue respondida y todo parecía estar bien. Luego vino Johannes Schaub e hizo una observación interesante :

Si pones un + antes del primer lambda, mágicamente comienza a funcionar.

Entonces, tengo curiosidad: ¿por qué funciona lo siguiente?

 int main() { auto test = +[]{}; // Note the unary operator + before the lambda test = []{}; } 

Comstack bien tanto con GCC 4.7+ como con Clang 3.2+. Es el código estándar conforme?

Sí, el código es conforme estándar. El + desencadena una conversión a un puntero de función antiguo simple para el lambda.

Lo que sucede es esto:

El comstackdor ve el primer lambda ( []{} ) y genera un objeto de cierre de acuerdo con §5.1.2. Como la lambda es una lambda no capturable , se aplica lo siguiente:

5.1.2 Expresiones lambda [expr.prim.lambda]

6 El tipo de cierre para una expresión lambda sin captura lambda tiene una función de conversión de const no explícita no virtual pública para señalar a la función que tiene el mismo parámetro y tipos de retorno que el operador de llamada de función del tipo de cierre. El valor devuelto por esta función de conversión será la dirección de una función que, cuando se invoca, tiene el mismo efecto que invocar al operador de llamada de función del tipo de cierre.

Esto es importante ya que el operador unario + tiene un conjunto de sobrecargas integradas, específicamente esta:

13.6 Operadores incorporados [over.built]

8 Para cada tipo T existen funciones del operador candidato de la forma

T* operator+(T*);

Y con esto, queda bastante claro lo que sucede: cuando el operador + se aplica al objeto de cierre, el conjunto de candidatos incorporados sobrecargados contiene una conversión a cualquier puntero y el tipo de cierre contiene exactamente un candidato: la conversión al puntero de función de la lambda.

El tipo de test en auto test = +[]{}; por lo tanto, se deduce a void(*)() . Ahora la segunda línea es fácil: para el segundo objeto lambda / cierre, una asignación al puntero de función desencadena la misma conversión que en la primera línea. Aunque la segunda lambda tiene un tipo de cierre diferente, el puntero de función resultante es, por supuesto, compatible y puede asignarse.