J'ai le code suivant:
#include <iostream> #include <string> #include <array> #include <map> #include <functional> template<typename T> struct tag {}; template <typename LambdaType, typename=void> struct split { split(LambdaType &&f) { std::cout << "[]()" << std::endl; } }; template <typename RetType, typename... ArgTypes> struct split<std::function<RetType(ArgTypes...)>> { split(std::function<RetType(ArgTypes...)> &&f) { std::cout << "std::function" << std::endl; }; }; template <typename RetType, typename... ArgTypes> struct split<RetType(*)(ArgTypes...)> { split(RetType(*f)(ArgTypes...)) { std::cout << "func-ptr" << std::endl; }; }; void f1(int) {}; int main(int argc, char **argv) { new split<std::decay<decltype(f1)>::type>(f1); new split<std::function<void(int)>>(std::function<void(int)>([](int) {})); /* how can I extract the argument type template pack from lambda ? */ new split([](int){}); return 0; }
Il y a 2 spécialisations pour split
, une pour std :: function
et un pour RetType (*) (ArgTypes ...)
. Pour les deux spécialisations, j'obtiens l'argument de modèle RetType
et ArgTypes ...
et le pack par correspondance de modèle. Cependant, je me demande s'il y a un moyen de faire la même chose avec un lambda
comme argument?
Comment puis-je extraire RetType
et ArgTypes. ..
d'un lambda dans une spécialisation pour la ligne new split ([] (int) {})
?
3 Réponses :
Vous pouvez utiliser la déduction des arguments de classe de modèle avec std::function
:
template<class F> function(F) -> function</*see below*/>;
Une fois que vous avez la std :: function
correspondante à votre lambda, vous pouvez récupérer les types de retour et d'argument en utilisant la spécialisation de modèle.
Cela fonctionne car std :: function
a un guide de déduction :
template <typename LambdaType, typename=void> struct split { using StdFunctionType = decltype(std::function{std::declval<LambdaType>()}); };
Si
decltype (& F :: operator ())
est de la formeR (G :: *) (A ...)
(éventuellementcv
-qualifié, éventuellementnoexcept
, éventuellement lvalue référence qualifiée) pour un type de classeG
, alors le type déduit eststd :: function
. Cette surcharge ne participe à la résolution de la surcharge que si& F :: operator ()
est bien formé lorsqu'il est traité comme un opérande non évalué.
Vous pouvez passer par une sorte de supercherie, par exemple:
#include <type_traits> template <typename LambdaType, typename=void> struct split { split(LambdaType &&f) { deduce(&LambdaType::operator()); } template<class RET, class CLOSURE, class... ARGS> void deduce(RET(CLOSURE::*)(ARGS...) const) { // You have your return and args here } }; template <typename RetType, typename... ArgTypes> struct split<RetType(*)(ArgTypes...)> { split(RetType(*f)(ArgTypes...)); }; void f1(int) {}; int main(int argc, char **argv) { split<std::decay_t<decltype(f1)>>{f1}; /* how can I extract the argument type template pack from lambda ? */ split([](int){}); return 0; }
Petite (tellement petite) mise en garde, cela ne fonctionne pas (directement) avec mutable
lambda en raison de la const
-qualification de l'opérateur d'appel dans deduce () < / code>.
Question: Existe-t-il un prédicat de trait de type pour tester si un type est un lambda?
J'ai ajouté une méthode que j'ai trouvée comme réponse ci-dessous. J'utilise le sous-classement du cas de spécialisation pour l'opérateur lambdas () ... C'est un peu comme votre méthode mais en utilisant des sous-classes.
J'ai trouvé une méthode qui utilise la sous-classification de la spécialisation (vue ici ):
/* g++ -std=c++17 */ #include <iostream> #include <string> #include <array> #include <map> #include <functional> template<typename T> struct tag {}; struct mybase {}; /* subclass specialization on type of operator() of lambda: */ template<class Ld> struct split : split<decltype(&Ld::operator())> { split(Ld &&f) : split<decltype(&Ld::operator())>(std::forward<Ld>(f)) {}; }; template <typename RetType, typename... ArgTypes> struct split<std::function<RetType(ArgTypes...)>> { split(std::function<RetType(ArgTypes...)> &&f) { std::cout << "std::function" << std::endl; }; }; template <typename RetType, typename... ArgTypes> struct split<RetType(*)(ArgTypes...)> { split(RetType(*f)(ArgTypes...)) { std::cout << "func-ptr" << std::endl; }; }; template <typename RetType, class Cls, typename... ArgTypes> struct split<RetType(Cls::*)(ArgTypes...) const > { split(const Cls &&f) { std::cout << "[]() const" << std::endl; }; }; template <typename RetType, class Cls, typename... ArgTypes> struct split<RetType(Cls::*)(ArgTypes...) > { split(Cls &&f) { std::cout << "[]()" << std::endl; }; }; void f1(int) {}; int main(int argc, char **argv) { new split<std::decay<decltype(f1)>::type>(f1); new split<std::function<void(int)>>(std::function<void(int)>([](int) {})); /* no g++-17: */ //auto l = [](int){}; //new split<decltype(l)>(std::forward<decltype(l)>(l)); /* g++-17: */ new split([](int){}); return 0; }
Vous pouvez obtenir le type de retour en utilisant ceci . Je ne sais pas si vous pouvez obtenir le (s) type (s) d'argument
Vous pouvez essayer SFINAE sur is_convertible en
RetType (*) (ArgTypes ...)
. Cela marcherait-il pour toi? De cette façon, vous gérerez également des lambdas sans captivité.@Barry Duplicate consiste à déduire le type de retour d'une fonction, tandis que cette question consiste à déduire à la fois le type de retour et les types d'argument d'un type lambda (fermeture).
@Barry (je ne voulais pas rouvrir seul sans discussion, je voulais voter ... mais j'ai oublié que dupe-hammer vient avec reopen-hammer.)
@Holt Heh, je sais bien - les problèmes du marteau ... Mais de toute façon, je pense que cette réponse répond à cette question. Et pour ce que ça vaut - la partie de cette réponse qui ne répond pas à cette question n'est pas non plus incluse dans votre réponse, non?
@Barry Seule la première réponse de la dupe s'applique, et même dans ce cas, elle est principalement ciblée sur le type de retour. Cependant, je conviens que ma réponse n'est pas si meilleure, mais celle de SergyA peut ajouter quelque chose. Si les OP trouvent la dupe suffisante, alors trompons-la.
@KonradEisele ceci répond à votre question? Sinon, pourriez-vous clarifier comment vous souhaitez utiliser les types de retour / arguments afin que nous puissions développer les réponses ici.