Comment convertir un lambda en une fonction std :: en utilisant des modèles

Fondamentalement, ce que je veux pouvoir faire, c’est prendre un lambda avec n’importe quel nombre de parameters et le convertir en une fonction std ::. J’ai essayé ce qui suit et aucune méthode ne fonctionne.

std::function([](){});//Complains that std::function is missing template parameters template  void foo(function f){} foo([](){});//Complains that it cannot find a matching candidate 

Le code suivant fonctionne cependant, mais ce n’est pas ce que je veux, car il nécessite de spécifier explicitement les parameters du modèle qui ne fonctionnent pas pour le code générique.

 std::function([](){}); 

J’ai bricolé avec des fonctions et des modèles toute la soirée et je n’arrive pas à comprendre, alors toute aide serait très appréciée.

Comme mentionné dans un commentaire, la raison pour laquelle j’essaie de le faire est que j’essaie d’implémenter le curry en C ++ en utilisant des modèles variadiques. Malheureusement, cela échoue horriblement lorsque vous utilisez lambdas. Par exemple, je peux passer une fonction standard en utilisant un pointeur de fonction.

 template  void foo(R (*f)(A...)) {} void bar() {} int main() { foo(bar); } 

Cependant, je n’arrive pas à comprendre comment passer un lambda à une telle fonction. Pourquoi je suis intéressé par la conversion d’un lambda générique en une std :: function parce que je peux faire ce qui suit, mais cela finit par exiger que j’indique explicitement les parameters du template à std :: function, ce que j’essaie d’éviter.

 template  void foo(std::function) {} int main() { foo(std::function([](){})); } 

Vous ne pouvez pas passer d’object de fonction lambda en tant qu’argument de type std::function sans spécifier explicitement l’argument de modèle T La déduction de type de modèle tente de faire correspondre le type de votre fonction lambda à la fonction std::function ce qu’elle ne peut pas faire dans ce cas – ces types ne sont pas les mêmes. La déduction de type de modèle ne prend pas en compte les conversions entre types.

Il est possible si vous pouvez lui donner une autre manière de déduire le type. Vous pouvez le faire en encapsulant l’argument de fonction dans un type d’ identity afin qu’il n’échoue pas en essayant de faire correspondre le lambda à std::function (car les types dépendants sont simplement ignorés par la déduction de type) et en donnant d’autres arguments.

 template  struct identity { typedef T type; }; template  void func(typename identity>::type f, T... values) { f(values...); } int main() { func([](int x, int y, int z) { std::cout << (x*y*z) << std::endl; }, 3, 6, 8); return 0; } 

Ce n'est évidemment pas utile dans votre situation cependant, car vous ne voulez pas transmettre les valeurs plus tard.

Comme vous ne voulez pas spécifier les parameters du modèle, et que vous ne voulez pas non plus passer d’autres arguments à partir desquels les parameters du modèle peuvent être déduits, le compilateur ne pourra pas déduire le type de votre argument std::function .

Vous pouvez utiliser une dissortingbution dédiée / rétrospective . Une fois que vous avez un outil comme celui-ci

 #include  using namespace std; template struct memfun_type { using type = void; }; template struct memfun_type { using type = std::function; }; template typename memfun_type::type FFL(F const &func) { // Function from lambda ! return func; } 

vous pouvez dire FFL() à tous les types lambda pour les convertir en la version correcte de std::function

 template  void Callback(std::function f){ // store f and call later } int main() { Callback(FFL([](int a, float b){ // do something })); return 0; } 

Afficher

Comme le montre la signature d’appel d’un lambda ou appelable arbitraire pour “make_function” , vous pouvez déduire la signature d’appel d’un lambda (ou de tout autre foncteur avec une signature d’appel unique) de son operator() unique) operator() :

 template struct remove_class { }; template struct remove_class { using type = R(A...); }; template struct remove_class { using type = R(A...); }; template struct remove_class { using type = R(A...); }; template struct remove_class { using type = R(A...); }; template struct get_signature_impl { using type = typename remove_class< decltype(&std::remove_reference::type::operator())>::type; }; template struct get_signature_impl { using type = R(A...); }; template struct get_signature_impl { using type = R(A...); }; template struct get_signature_impl { using type = R(A...); }; template using get_signature = typename get_signature_impl::type; 

C’est une approche plutôt inflexible; comme le dit R. Martinho Fernandes, cela ne fonctionnera pas pour les foncteurs à operator() multiples operator() , ni pour les foncteurs avec operator() structuré operator() ou pour les lambda polymorphes (C ++ 14). C’est la raison pour laquelle bind limite l’inférence de son type de résultat jusqu’à la tentative d’appel éventuelle.

Il est possible d’obtenir le type de fonction std :: nécessaire pour lambda en utilisant une dérivation, un decltype, des modèles variadiques et quelques traits de type:

 namespace ambient { template  struct function_traits : public function_traits {}; template  struct function_traits { typedef ReturnType (*pointer)(Args...); typedef const std::function function; }; template  typename function_traits::function to_function (Function& lambda) { return static_cast::function>(lambda); } template  struct overload_lambda : L { overload_lambda(L l) : L(l) {} template  void operator()(T&& ... values){ // here you can access the target std::function with to_function(*(L*)this)(std::forward(values)...); } }; template  overload_lambda lambda(L l){ return overload_lambda(l); } } 

Je l’utilise dans mon code comme ceci:

ambient::lambda([&](const vector& val){ // some code here // })(a);

PS: dans mon cas réel, je sauvegarde ensuite cet object std :: function et ses arguments dans un kernel d’objects génériques que je pourrai exécuter ultérieurement à la demande via des fonctions virtuelles.

Le currying n’est-il pas déjà implémenté avec std::bind ?

 auto sum = [](int a, int b){ return a+b; }; auto inc = std::bind( sum, _1, 1 ); assert( inc(1)==2 ); 

Cela pourrait être intéressant pour vous: https://gist.github.com/Manu343726/94769034179e2c846acc

C’est une expérience que j’ai écrite il y a un mois. Le but était de créer un template C ++ de type foncteur qui émule les fermetures d’appels partielles de Haskell, c’est-à-dire la création automatique d’une fermeture de mn lorsque vous appelez avec n argumments une fonction avec m parameters.

Ceci est un exemple de ce que cette expérience est cappable à faire:

 int f( int a, int b, int c, int d) { return a+b+c+d; } int main() { auto foo = haskell::make_function( f ); auto a = foo , 1 , 2 , 3; //a is a closure function object with one parameter std::cout << a , 4 << std::endl; //Prints 10 } 

haskell::make_function utilise des caractères de type pour prendre en charge les différents types d'entités de fonction, lambdas inclus:

 auto f = haskell::make_function( []( int x, int y , int z ){ return x*y*z; } ); auto a = f(1,2); //a is functor with one parameter (Using the alternative C++-like syntax) auto b = a(3); // b is 6 

Comme vous pouvez le voir, j'utilise l'opérateur de virgule dans la syntaxe mmimic Haskell, mais vous pouvez le changer pour l'opérateur d'appel afin d'atteindre la syntaxe de votre objective.

Vous êtes complètement libre de faire tout ce que vous voulez avec le code (Vérifiez la licence).