web-dev-qa-db-fra.com

Une raison de ne pas utiliser les lambdas globales?

Nous avions une fonction qui utilisait une lambda interne non capturante, par exemple:

void foo() {
  auto bar = [](int a, int b){ return a + b; }

  // code using bar(x,y) a bunch of times
}

Maintenant, la fonctionnalité implémentée par le lambda est devenue nécessaire ailleurs, donc je vais sortir le lambda de foo() dans la portée globale/namespace. Je peux soit le laisser comme lambda, ce qui en fait une option copier-coller, soit le changer pour une fonction appropriée:

auto bar = [](int a, int b){ return a + b; } // option 1
int bar(int a, int b){ return a + b; } // option 2

void foo() {
  // code using bar(x,y) a bunch of times
}

Le changer en une fonction appropriée est trivial, mais cela m'a fait me demander s'il y avait une raison pas de le laisser comme lambda? Y a-t-il une raison de ne pas utiliser les lambdas partout au lieu des fonctions globales "normales"?

89
Baruch

Il y a une raison très importante de ne pas utiliser de lambdas globaux: parce que ce n'est pas normal.

La syntaxe de fonction régulière de C++ existe depuis l'époque de C. Les programmeurs savent depuis des décennies ce que signifie cette syntaxe et comment ils fonctionnent (bien qu'il soit vrai que toute cette fonction de décomposition de la fonction vers le pointeur mord parfois même les programmeurs chevronnés). Si un programmeur C++ de n'importe quel niveau de compétence au-delà du "débutant absolu" voit une définition de fonction, il sait ce qu'il obtient.

Un lambda global est une bête complètement différente. Il a un comportement différent d'une fonction régulière. Les lambdas sont des objets, contrairement aux fonctions. Ils ont un type, mais ce type est distinct du type de leur fonction. Et ainsi de suite.

Alors maintenant, vous avez élevé la barre en communiquant avec d'autres programmeurs. Un programmeur C++ doit comprendre les lambdas s'il veut comprendre ce que fait cette fonction. Et oui, nous sommes en 2019, donc un programmeur C++ décent devrait avoir une idée de ce à quoi ressemble un lambda. Mais c'est toujours une barre plus élevée.

Et même s'ils le comprennent, la question dans l'esprit de ce programmeur sera ... pourquoi l'auteur de ce code l'a-t-il écrit de cette façon? Et si vous n'avez pas une bonne réponse à cette question (par exemple, parce que vous explicitement voulez pour interdire la surcharge, comme dans les points de personnalisation des plages), alors vous devez utiliser le mécanisme commun.

Préférez les solutions attendues aux nouvelles, le cas échéant. Utilisez la méthode la moins compliquée pour faire passer votre message.

61
Nicol Bolas

Je peux penser à quelques raisons pour lesquelles vous voudriez éviter les lambdas globaux comme remplacements directs pour les fonctions régulières:

  • les fonctions régulières peuvent être surchargées; les lambdas ne le peuvent pas (il existe cependant des techniques pour le simuler)
  • Malgré le fait qu'elles sont fonctionnelles, même un lambda non capturant comme celui-ci occupera de la mémoire (généralement 1 octet pour la non capture).
    • comme indiqué dans les commentaires, les compilateurs modernes optimiseront ce stockage sous la règle comme si

"Pourquoi ne devrais-je pas utiliser des lambdas pour remplacer des foncteurs avec état (classes)?"

  • les classes ont simplement moins de restrictions que les lambdas et devraient donc être la première chose que vous atteignez pour
    • (données publiques/privées, surcharge, méthodes d'assistance, etc.)
  • si le lambda a un état, il est d'autant plus difficile de raisonner quand il devient global.
    • Nous devrions préférer créer un instance d'une classe à la portée la plus étroite possible
  • il est déjà difficile de convertir un lambda non capturant en un pointeur de fonction, et il est impossible pour un lambda qui spécifie quoi que ce soit dans sa capture.
    • les classes nous donnent un moyen simple de créer des pointeurs de fonction, et ils sont aussi ce que de nombreux programmeurs sont plus à l'aise avec
  • Les lambdas avec n'importe quelle capture ne peuvent pas être construits par défaut (en C++ 20. Auparavant, il n'y avait aucun constructeur par défaut)
53
AndyG

Après avoir demandé, j'ai pensé à une raison pour ne pas le faire: comme ce sont des variables, elles sont sujettes à Fiasco d'ordre d'initialisation statique ( https : //isocpp.org/wiki/faq/ctors#static-init-order ), ce qui pourrait provoquer des bugs sur toute la ligne.

9
Baruch

Y a-t-il une raison de ne pas utiliser les lambdas partout au lieu des fonctions globales "normales"?

Un problème d'un certain niveau de complexité nécessite une solution au moins de la même complexité. Mais s'il existe une solution moins complexe pour le même problème, alors il n'y a vraiment aucune justification pour utiliser le plus complexe. Pourquoi introduire une complexité dont vous n'avez pas besoin?

Entre un lambda et une fonction, une fonction est simplement le type d'entité le moins complexe des deux. Vous n'avez pas à justifier de ne pas utiliser de lambda. Vous devez justifier d'en utiliser un. Une expression lambda introduit un type de fermeture, qui est un type de classe sans nom avec toutes les fonctions membres spéciales habituelles, un opérateur d'appel de fonction et, dans ce cas, un opérateur de conversion implicite vers le pointeur de fonction, et crée un objet de ce type. L'initialisation de la copie d'une variable globale à partir d'une expression lambda fait simplement beaucoup plus que la simple définition d'une fonction. Il définit un type de classe avec six fonctions implicitement déclarées, définit deux autres fonctions d'opérateur et crée un objet. Le compilateur doit faire beaucoup plus. Si vous n'avez besoin d'aucune des fonctionnalités d'un lambda, alors n'utilisez pas de lambda…

8
Michael Kenzel

Les lambdas sont fonctions anonymes .

Si vous utilisez un lambda nommé, cela signifie que vous utilisez essentiellement une fonction anonyme nommée. Pour éviter cet oxymore, vous pourriez aussi bien utiliser une fonction.

6
Eric Duminil

s'il y a une raison de ne pas le laisser comme lambda? Y a-t-il une raison de ne pas utiliser les lambdas partout au lieu des fonctions globales "normales"?

Nous avions l'habitude d'utiliser des fonctions au lieu du foncteur global, donc cela casse la cohérence et le Principe du moindre étonnement.

Les principales différences sont:

  • les fonctions peuvent être surchargées, contrairement aux foncteurs.
  • les fonctions peuvent être trouvées avec ADL, pas avec les foncteurs.
5
Jarod42