Est-il possible de passer une fonction lambda en tant que pointeur de fonction? Si c'est le cas, je dois faire quelque chose de mal car je reçois une erreur de compilation.
Considérez l'exemple suivant
using DecisionFn = bool(*)();
class Decide
{
public:
Decide(DecisionFn dec) : _dec{dec} {}
private:
DecisionFn _dec;
};
int main()
{
int x = 5;
Decide greaterThanThree{ [x](){ return x > 3; } };
return 0;
}
Lorsque j'essaie de compiler cela , j'obtiens l'erreur de compilation suivante:
In function 'int main()':
17:31: error: the value of 'x' is not usable in a constant expression
16:9: note: 'int x' is not const
17:53: error: no matching function for call to 'Decide::Decide(<brace-enclosed initializer list>)'
17:53: note: candidates are:
9:5: note: Decide::Decide(DecisionFn)
9:5: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'DecisionFn {aka bool (*)()}'
6:7: note: constexpr Decide::Decide(const Decide&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'const Decide&'
6:7: note: constexpr Decide::Decide(Decide&&)
6:7: note: no known conversion for argument 1 from 'main()::<lambda()>' to 'Decide&&'
C'est un sacré message d'erreur à digérer, mais je pense que ce que j'en retire, c'est que le lambda ne peut pas être traité comme un constexpr
donc je ne peux donc pas le passer comme pointeur de fonction? J'ai aussi essayé de faire x
const, mais cela ne semble pas aider.
c++
c++11
lambda
function-pointers
Cory Kramer
la source
la source
Réponses:
Un lambda ne peut être converti en pointeur de fonction que s'il ne capture pas, à partir du projet de section standard C ++ 11
5.1.2
[expr.prim.lambda] dit ( accent sur le mien ):Remarque, cppreference couvre également cela dans leur section sur les fonctions Lambda .
Les alternatives suivantes fonctionneraient donc:
et il en serait de même:
et comme le souligne 5gon12eder , vous pouvez également l'utiliser
std::function
, mais notez questd::function
c'est un poids lourd , donc ce n'est pas un compromis moins coûteux.la source
void*
comme seul paramètre. Il est normalement appelé "pointeur utilisateur". Il est également relativement léger, mais a tendance à nécessiter de l'malloc
espace.La réponse de Shafik Yaghmour explique correctement pourquoi le lambda ne peut pas être passé en tant que pointeur de fonction s'il a une capture. Je voudrais montrer deux correctifs simples pour le problème.
Utilisez à la
std::function
place des pointeurs de fonction bruts.Ceci est une solution très propre. Notez cependant qu'il inclut une surcharge supplémentaire pour l'effacement de type (probablement un appel de fonction virtuelle).
Utilisez une expression lambda qui ne capture rien.
Étant donné que votre prédicat n'est vraiment qu'une constante booléenne, les éléments suivants contourneraient rapidement le problème actuel. Voir cette réponse pour une bonne explication pourquoi et comment cela fonctionne.
la source
glutReshapeFunc
.std::function
ou un lambda - pourquoi ne le feraient-ils pas? À tout le moins, c'est une syntaxe plus lisible. Habituellement, il faut utiliser un pointeur de fonction pour interagir avec les bibliothèques C (en fait, avec n'importe quelle bibliothèque externe) , et bien sûr vous ne pouvez pas le modifier pour accepter une fonction std :: ou lambda.Les expressions lambda, même capturées, peuvent être traitées comme un pointeur de fonction (pointeur vers une fonction membre).
C'est délicat car une expression lambda n'est pas une simple fonction. Il s'agit en fait d'un objet avec un opérateur ().
Lorsque vous êtes créatif, vous pouvez l'utiliser! Pensez à une classe "function" dans le style de std :: function. Si vous enregistrez l'objet, vous pouvez également utiliser le pointeur de fonction.
Pour utiliser le pointeur de fonction, vous pouvez utiliser les éléments suivants:
Pour construire une classe qui peut commencer à fonctionner comme une "std :: function", vous avez d'abord besoin d'une classe / struct qui peut stocker un objet et un pointeur de fonction. Vous avez également besoin d'un opérateur () pour l'exécuter:
Avec cela, vous pouvez maintenant exécuter des lambdas capturés et non capturés, tout comme vous utilisez l'original:
Ce code fonctionne avec VS2015
Mise à jour 04.07.17:
la source
operator()
implémentation, donc si je le lis correctement, je ne pense pas que cela fonctionnerait pour appeler le lambda à l'aide d'un pointeur de fonction de style C , n'est-ce pas? C'est ce que la question initiale demandait.La capture de lambdas ne peut pas être convertie en pointeurs de fonction, comme l'a souligné cette réponse .
Cependant, il est souvent assez difficile de fournir un pointeur de fonction à une API qui n'en accepte qu'un. Pour ce faire, la méthode la plus souvent citée consiste à fournir une fonction et à appeler un objet statique avec elle.
C'est fastidieux. Nous poussons cette idée plus loin et automatisons le processus de création
wrapper
et nous facilitons la vie.Et utilisez-le comme
Vivre
Il s'agit essentiellement de déclarer une fonction anonyme à chaque occurrence de
fnptr
.Notez que les invocations de
fnptr
écraser lescallable
callables donnés précédemment écrites du même type. Nous y remédions, dans une certaine mesure, avec leint
paramètreN
.la source
Un raccourci pour utiliser un lambda avec comme pointeur de fonction C est le suivant:
Utilisation de Curl comme exemple ( informations de débogage curl )
la source
+
astuce vous apporte).Bien que l'approche par modèle soit intelligente pour diverses raisons, il est important de se souvenir du cycle de vie du lambda et des variables capturées. Si une forme quelconque de pointeur lambda est utilisée et que le lambda n'est pas une continuation vers le bas, alors seul un lambda de copie [=] doit être utilisé. C'est-à-dire, même dans ce cas, la capture d'un pointeur vers une variable de la pile n'est PAS SÉCURISÉE si la durée de vie de ces pointeurs capturés (déroulement de la pile) est plus courte que la durée de vie du lambda.
Une solution plus simple pour capturer un lambda en tant que pointeur est:
par exemple,
new std::function<void()>([=]() -> void {...}
N'oubliez pas de le faire plus tard
delete pLamdba
afin de ne pas divulguer la mémoire lambda. Le secret à réaliser ici est que les lambdas peuvent capturer des lambdas (demandez-vous comment cela fonctionne) et aussi que pourstd::function
fonctionner de manière générique, l'implémentation lambda doit contenir suffisamment d'informations internes pour donner accès à la taille des données lambda (et capturées) ( c'est pourquoi celadelete
devrait fonctionner [exécuter des destructeurs de types capturés]).la source
new
- std :: stocke déjà le lambda sur le tas ET évite d'avoir à se rappeler d'appeler delete.Pas une réponse directe, mais une légère variation pour utiliser le modèle de modèle "functor" pour cacher les spécificités du type lambda et garder le code agréable et simple.
Je n'étais pas sûr de savoir comment vous vouliez utiliser la classe decide, j'ai donc dû étendre la classe avec une fonction qui l'utilise. Voir l'exemple complet ici: https://godbolt.org/z/jtByqE
La forme de base de votre classe pourrait ressembler à ceci:
Où vous passez le type de la fonction dans le cadre du type de classe utilisé comme:
Encore une fois, je ne savais pas pourquoi vous capturez,
x
il était plus logique (pour moi) d'avoir un paramètre que vous transmettez au lambda) afin que vous puissiez utiliser comme:Voir le lien pour un exemple complet
la source
Comme cela a été mentionné par les autres, vous pouvez remplacer la fonction Lambda au lieu du pointeur de fonction. J'utilise cette méthode dans mon interface C ++ vers le solveur F77 ODE RKSUITE.
la source