262 votes

Un lambda positif : '+[]{}' - Quelle sorcellerie est-ce là ?

Dans la question Stack Overflow La redéfinition des lambdas n'est pas autorisée en C++11, pourquoi ? on a donné un petit programme qui ne compile pas :

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

On a répondu à la question et tout semble aller bien. Puis vint Johannes Schaub et a fait une observation intéressante :

Si vous mettez un + avant la première lambda, cela commence à fonctionner comme par magie.

Je suis donc curieux : pourquoi ce qui suit fonctionne-t-il ?

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

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

242voto

Daniel Frey Points 30752

Oui, le code est conforme aux normes. Le site + déclenche une conversion en un bon vieux pointeur de fonction pour le lambda.

Voici ce qui se passe :

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 non-capture lambda, ce qui suit s'applique :

5.1.2 Les expressions lambda [expr.prim.lambda]

6 Le type de fermeture d'un expression lambda sans Capture lambda possède une fonction publique non virtuelle non explicite de conversion const vers un pointeur vers une fonction ayant les mêmes types de paramètres 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'invocation de l'opérateur d'appel de fonction du type de fermeture.

Ceci est important car l'opérateur unaire + possède un ensemble de surcharges intégrées, notamment celle-ci :

13.6 Opérateurs incorporés [over.built].

8 Pour chaque type T il existe des fonctions d'opérateur candidates de la forme

    T* operator+(T*);

Et avec ça, ce qui se passe est assez clair : Quand l'opérateur + est appliqué à l'objet de fermeture, l'ensemble des candidats intégrés surchargés contient une conversion vers un pointeur de fonction et le type de fermeture contient exactement un candidat : la conversion vers le pointeur de fonction de la lambda.

Le type de test en auto test = +[]{}; est donc déduit de void(*)() . Maintenant, la deuxième ligne est facile : pour le deuxième objet lambda/fermeture, 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 sûr, compatible et peut être assigné.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X