Un lambda positif: ‘+ {}’ – Quelle est la sorcellerie?

Dans Stack Overflow question La redéfinition de lambdas n’est pas autorisée dans C ++ 11, pourquoi? , un petit programme a été donné qui ne comstack pas:

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

La question a été répondu et tout semblait bien. Puis est venu Johannes Schaub et a fait une observation intéressante :

Si vous mettez un + avant le premier lambda, cela commence à fonctionner comme par magie.

Donc, je suis curieux: pourquoi le suivant fonctionne-t-il?

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

Il comstack bien avec GCC 4.7+ et Clang 3.2+. Le code standard est-il conforme?

Oui, le code est conforme à la norme. Le + déclenche une conversion en un ancien pointeur de fonction simple pour le lambda.

Qu’est-ce qui se passe est la suivante:

Le compilateur voit le premier lambda ( []{} ) et génère un object de fermeture conformément au §5.1.2. Comme le lambda est un lambda non capturant , ce qui suit s’applique:

5.1.2 Expressions Lambda [expr.prim.lambda]

6 Le type de fermeture pour une expression lambda sans capture lambda a une fonction de conversion const non virtuelle publique non explicite en pointeur vers une fonction ayant les mêmes types de paramètre et de retour que l’opérateur d’appel de fonction du type de fermeture. La valeur renvoyée par cette fonction de conversion doit être l’adresse d’une fonction qui, lorsqu’elle est invoquée, a le même effet que l’appel de l’opérateur d’appel de fonction du type de fermeture.

Ceci est important car l’opérateur unaire + a un ensemble de surcharges intégrées, en particulier celui-ci:

13.6 Opérateurs intégrés [over.built]

8 Pour tout type T il existe des fonctions opérateur candidates de la forme

T* operator+(T*);

Et avec cela, il est clair ce qui se passe: lorsque operator + est appliqué à l’object de fermeture, l’ensemble des candidats intégrés surchargés contient une conversion en tout pointeur et le type de fermeture contient exactement un candidat: la conversion en pointeur de fonction du lambda.

Le type de test en test auto test = +[]{}; est donc déduit du void(*)() . Maintenant, la deuxième ligne est facile: pour le deuxième object lambda / closure, une affectation au pointeur de fonction déclenche la même conversion que dans la première ligne. Bien que le second lambda ait un type de fermeture différent, le pointeur de fonction résultant est, bien sûr, compatible et peut être atsortingbué.