Je suis nouveau sur C ++ 11. J'écris la fonction lambda récursive suivante, mais elle ne se compile pas.
sum.cpp
#include <iostream>
#include <functional>
auto term = [](int a)->int {
return a*a;
};
auto next = [](int a)->int {
return ++a;
};
auto sum = [term,next,&sum](int a, int b)mutable ->int {
if(a>b)
return 0;
else
return term(a) + sum(next(a),b);
};
int main(){
std::cout<<sum(1,10)<<std::endl;
return 0;
}
erreur de compilation:
vimal @ linux-718q: ~ / Study / 09C ++ / c ++ 0x / lambda> g ++ -std = c ++ 0x sum.cpp
sum.cpp: Dans la fonction lambda: sum.cpp: 18: 36: erreur: ' ((<lambda(int, int)>*)this)-><lambda(int, int)>::sum
' ne peut pas être utilisé comme fonction
version gcc
gcc version 4.5.0 20091231 (expérimental) (GCC)
Mais si je change la déclaration de sum()
comme ci-dessous, cela fonctionne:
std::function<int(int,int)> sum = [term,next,&sum](int a, int b)->int {
if(a>b)
return 0;
else
return term(a) + sum(next(a),b);
};
Quelqu'un pourrait-il s'il vous plaît jeter la lumière là-dessus?
mutable
mot - clé là-bas?std::function<int(int,int)> sum = [&](int a, int b) {
Réponses:
Pensez à la différence entre la version automatique et la version de type entièrement spécifiée. Le mot clé auto déduit son type de tout ce avec quoi il est initialisé, mais ce avec quoi vous l'initialisez doit savoir quel est son type (dans ce cas, la fermeture lambda doit connaître les types qu'elle capture). Quelque chose d'un problème de poule et d'oeuf.
D'un autre côté, le type d'un objet fonction entièrement spécifié n'a pas besoin de "savoir" quoi que ce soit sur ce qui lui est assigné, et ainsi la fermeture du lambda peut également être pleinement informée des types qu'il capture.
Considérez cette légère modification de votre code et cela peut avoir plus de sens:
De toute évidence, cela ne fonctionnerait pas avec l' automobile . Les fonctions lambda récursives fonctionnent parfaitement bien (du moins elles le font dans MSVC, où j'ai de l'expérience avec elles), c'est juste qu'elles ne sont pas vraiment compatibles avec l'inférence de type.
la source
auto
variable dans l'initialiseur de celui-ci. le type de la variable automatique n'est pas encore connu lorsque l'initialiseur est en cours de traitement.sum
autre questd::function<int(int, int)>
, ou la spécification C ++ n'a-t-elle tout simplement pas pris la peine de l'inférer?L'astuce consiste à alimenter l'implémentation lambda en tant que paramètre , et non par capture.
Tous les problèmes en informatique peuvent être résolus par un autre niveau d'indirection . J'ai trouvé cette astuce pour la première fois sur http://pedromelendez.com/blog/2015/07/16/recursive-lambdas-in-c14/
Il ne nécessite C ++ 14 alors que la question est en C ++ 11, mais peut - être intéressant pour la plupart.
Passer via
std::function
est également possible, mais peut entraîner un code plus lent. Mais pas toujours. Jetez un œil aux réponses à std :: function vs templateCe n'est pas seulement une particularité du C ++, c'est un mappage direct avec les mathématiques du calcul lambda. De Wikipedia :
la source
function<>
. Je ne vois pas pourquoi quelqu'un le préférerait. Edit: C'est plus rapide apparemment.error: use of ‘[...]’ before deduction of ‘auto’
- nécessaire de spécifier explicitement le type de retour (d'un autre côté, n'a pas besoin de mutable).Avec C ++ 14, il est maintenant assez facile de faire un lambda récursif efficace sans avoir à subir la surcharge supplémentaire de
std::function
, en seulement quelques lignes de code (avec une petite modification de l'original pour empêcher l'utilisateur de prendre une copie accidentelle ):avec lequel votre
sum
tentative initiale devient:En C ++ 17, avec CTAD, nous pouvons ajouter un guide de déduction:
Ce qui évite la nécessité de la fonction d'assistance. Nous pouvons simplement écrire
y_combinator{[](auto self, ...){...}}
directement.En C ++ 20, avec CTAD pour les agrégats, le guide de déduction ne sera pas nécessaire.
la source
std::forward<decltype(sum)>(sum)
au lieu desum
sur la dernière ligne.operator()
donc il n'y a rien à gagner à transférersum
const
surcharge au cas où l'objet-fonction fourni a unconst
opérateur non -appel. Et utilisez SFINAE et calculénoexcept
pour les deux. De plus, il n'y a plus besoin de la fonction maker dans C ++ 17.auto sum
copie ... mais il copie unreference_wrapper
, ce qui revient à prendre une référence. Le faire une fois dans l'implémentation signifie qu'aucune des utilisations ne sera jamais copiée accidentellement.J'ai une autre solution, mais je ne travaille qu'avec des lambdas sans état:
L'astuce ici est que les lambdas peuvent accéder aux variables statiques et vous pouvez convertir celles sans état en pointeur de fonction.
Vous pouvez l'utiliser avec des lambdas standard:
Son travail dans GCC 4.7
la source
Vous pouvez faire en sorte qu'une fonction lambda s'appelle elle-même de manière récursive. La seule chose que vous devez faire est de le référencer via un wrapper de fonction afin que le compilateur sache son type de retour et d'argument (vous ne pouvez pas capturer une variable - le lambda lui-même - qui n'a pas encore été définie) .
Faites très attention à ne pas sortir de la portée du wrapper f.
la source
Pour rendre lambda récursif sans utiliser de classes et de fonctions externes (comme
std::function
ou un combinateur à virgule fixe), on peut utiliser la construction suivante en C ++ 14 ( exemple en direct ):imprime:
Remarque: le type de résultat lambda doit être spécifié explicitement.
la source
J'ai exécuté un benchmark comparant une fonction récursive à une fonction lambda récursive en utilisant la
std::function<>
méthode de capture. Avec des optimisations complètes activées sur la version 4.1 de clang, la version lambda fonctionnait beaucoup plus lentement.Produit des résultats:
(Remarque: j'ai également confirmé avec une version qui prenait les entrées de cin, afin d'éliminer l'évaluation du temps de compilation)
Clang produit également un avertissement du compilateur:
Ce qui est attendu et sûr, mais doit être noté.
C'est formidable d'avoir une solution dans nos ceintures d'outils, mais je pense que le langage aura besoin d'une meilleure façon de gérer ce cas si les performances doivent être comparables aux méthodes actuelles.
Remarque:
Comme l'a souligné un commentateur, il semble que la dernière version de VC ++ a trouvé un moyen d'optimiser cela au point de performances égales. Peut-être n'avons-nous pas besoin d'une meilleure façon de gérer cela, après tout (sauf pour le sucre syntaxique).
De plus, comme certains autres articles SO l'ont souligné ces dernières semaines, les performances en elles-
std::function<>
mêmes peuvent être la cause du ralentissement par rapport à la fonction d'appel directement, du moins lorsque la capture lambda est trop grande pour s'adapter à certainesstd::function
utilisations de l' espace optimisé par la bibliothèque pour les petits foncteurs. (Je suppose qu'un peu comme les diverses optimisations de chaînes courtes?).la source
Il s'agit d'une implémentation légèrement plus simple de l'opérateur de point de fixation qui rend un peu plus évident ce qui se passe.
la source
std::function
par un pointeur de fonction (des cœurs, cela ne fonctionnera qu'avec une fonction normale et des lambdas sans état). Btwfib_nonr
devrait accepterfixpoint<int,int>
, si vous utilisezstd::function
sa nouvelle copie à partir de*this
.Voici une version raffinée de la solution Y-combinator basée sur celle proposée par @Barry.
Pour l'utiliser, on pourrait faire ce qui suit
Il est similaire au
let rec
mot - clé dans OCaml, mais pas le même.la source
C ++ 14: Voici un ensemble générique récursif anonyme sans état / sans capture de lambdas qui renvoie tous les nombres de 1, 20
Si je comprends bien, cela utilise la solution du combinateur Y
Et voici la version somme (n, m)
la source
Voici la réponse finale pour le PO. Quoi qu'il en soit, Visual Studio 2010 ne prend pas en charge la capture de variables globales. Et vous n'avez pas besoin de les capturer car la variable globale est accessible globalement par define. La réponse suivante utilise plutôt une variable locale.
la source
Vous essayez de capturer une variable (somme) que vous êtes en train de définir. Ça ne peut pas être bon.
Je ne pense pas que des lambdas C ++ 0x véritablement auto-récursifs soient possibles. Cependant, vous devriez pouvoir capturer d'autres lambdas.
la source
Cette réponse est inférieure à celle des Yankes, mais quand même, la voici:
la source
reinterpret_cast
. Le meilleur moyen dans votre cas est probablement de créer une structure qui remplacedp_type
. Il doit avoir un champfp_type
, peut être construit à partir defp_type
et avoir un opérateur()
avec des arguments commefp_type
. Ce sera proche destd::function
mais permettra un argument d'auto-référencement.struct
ajouterait également un niveau supplémentaire d'indirection. L'exemple fonctionne et le casting est conforme aux normes, je ne sais pas à quoi cela-1
servait.-1
je ne savais pas qui vous l'a donné, mais je pense que c'est parce qu'ilreinterpret_cast
devrait être utilisé en dernier recours.cast
est censé fonctionner selon la norme c ++ 11. L'utilisation d'unstruct
, à mes yeux, pourrait vaincre l'utilisation d'un objet lambda. Après tout, le questruct
vous proposez est un foncteur, utilisant un objet lambda.std::function
et vous aurez quelque chose de proche de celui que j'avais en tête. Cela aura probablement des performances similaires à votre solution.Vous avez besoin d'un combinateur à virgule fixe. Regarde ça .
ou regardez le code suivant:
la source