web-dev-qa-db-fra.com

Un lambda positif: '+ [] {}' - De quelle sorcellerie s'agit-il?

Dans la question de débordement de pile redéfinir lambdas non autorisé en C++ 11, pourquoi?, un petit programme a été donné qui ne compiler:

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

La question a été répondue et tout semblait aller bien. Puis vint Johannes Schaub et fit ne observation intéressante :

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

Donc, je suis curieux: pourquoi fonctionne le travail suivant?

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

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

254
Daniel Frey

Oui, le code est conforme aux normes. 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 objet de fermeture conformément au § 5.1.2. Comme le lambda est un sans capture lambda, les règles suivantes s'appliquent:

5.1.2 Expressions lambda [expr.prim.lambda]

6 Le type de fermeture pour une expression lambda sans lambda-capture a une fonction de conversion const publique non virtuelle non virtuelle vers pointeur à fonction ayant le même paramètre et les mêmes types 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, une fois appelée, a le même effet que l’appel de l’opérateur d’appel à la 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 chaque type T, il existe des fonctions opérateurs candidates du formulaire

T* operator+(T*);

Et avec cela, il est assez clair ce qui se passe: lorsque l'opérateur + Est appliqué à l'objet de fermeture, l'ensemble des candidats intégrés surchargés contient un pointeur de conversion vers n'importe quel pointeur et le type de fermeture contient exactement un candidat. : La conversion au pointeur de fonction du lambda.

Le type de test dans auto test = +[]{}; Est donc déduit de void(*)(). Maintenant, la deuxième ligne est simple: pour le deuxième objet lambda/closing, une affectation au pointeur de fonction déclenche la même conversion que dans la première ligne. Même si le deuxième lambda a un type de fermeture différent, le pointeur de fonction résultant est bien entendu compatible et peut être attribué.

234
Daniel Frey