Comment pourrais-je stocker n'importe quel type de fonction avec n'importe quel nombre d'arguments dans une variable? Quelque chose comme ceci:
int add(int i, int j) { return i + j; } template<typename Function, typename... Args> class Class { private: std::function<Function(Args...)> function; public: Class(Function _function, Args... _args) { Now what? } }; int main() { Class test(add, 1, 2); test.function(); }
5 Réponses :
Vous pouvez essayer quelque chose comme ça
#include <iostream> #include <functional> #include <tuple> int add(int i, int j) { return i + j; } template<typename Function, typename... Args> class Class { private: Function function_; std::tuple<Args...> args; public: Class(Function _function, Args... _args) : function_ { std::forward<Function>(_function) } , args{std::forward<Args>(_args)...} {} auto function() { return std::apply(function_,args); } }; int main() { Class test(add, 1, 2); std::cout<< test.function() << std::endl ; }
Réinvente std :: function
!
Eh bien, je n'ai pas pensé à lier. L'avantage de ma solution est qu'il peut changer les paramètres plus tard. Mais je suis d'accord que votre solution est meilleure.
Ma solution peut se relier comme la vôtre
Nécessite d'initialiser le tuple directement, pas avec _
.
C'est à cela que sert bind
!
Votre Classe
est en fait juste std :: bind
. Imaginons que vous en ayez une autre utilisation, et faites en sorte qu'il encapsule la logique std :: bind
. Nous devons donc stocker une std :: function
qui n'accepte aucun autre argument, et renvoie (dans ce cas) int
… mais nous la rendrons générique ( ReturnType
).
Ensuite, lors de l'initialisation, vous "liez" les arguments avec lesquels vous avez construit Class
, et les transférez dans la std :: function code> (l'utilisation de
std :: forward
permettant de déplacer la sémantique pour des types d'arguments plus complexes).
#include <functional> #include <iostream> int add(int i, int j) { return i + j; } int main() { auto func = std::bind(add, 1, 2); std::cout << func() << '\n'; } // Output: 3
Ou, si vous n'avez pas besoin de Class code > pour faire autre chose, c'est juste:
#include <functional> #include <iostream> int add(int i, int j) { return i + j; } template <typename Function, typename... Args> class Class { private: using ReturnType = std::invoke_result_t<Function, Args...>; std::function<ReturnType()> function; public: Class(Function _function, Args... _args) : function(std::bind(_function, std::forward<Args>(_args)...)) {} auto Call() { return function(); } }; int main() { Class test(add, 1, 2); std::cout << test.Call() << '\n'; } // Output: 3
Votre type de std::function
est faux car Function
est le type de la fonction entière, mais vous l'avez utilisé comme un type de retour.
-1: n'utilisez jamais bind dans le code générique. Si les arguments sont des expressions de liaison / des espaces réservés, les choses deviennent complètement folles d'une manière que personne qui n'a pas implémenté bind ne comprendra probablement. Avoir un cas de coin explosif sans raison valable est une mauvaise idée en code C ++ débutant, intermédiaire ou avancé; et l'éviter avec bind est une vraie douleur.
@ Yakk-AdamNevraumont Je n'ai pas encore implémenté bind, je n'ai jamais eu de problème du genre auquel vous faites référence. Pourriez-vous fournir un exemple?
@LightnessRacesinOrbit Que se passe-t-il si vous transmettez le résultat de std :: bind
à std :: bind
? "Si l'argument stocké arg est de type T pour lequel std :: is_bind_expression std :: bind
, ce qui signifie que le code d'espace réservé / composition entre en jeu, et cela peut se casser de manière ridicule.
Dans C ++ 11 et supérieur, vous pouvez utiliser des fermetures Lambda. Elles font exactement ce que vous décrivez.
Par exemple:
struct TestFunctor { int a; int b; TestFunctor(int a, int b):a(a), b(b){} int operator()(){return a+b;} }; int a = 1; int b = 2; int c = TestFunctor(a,b)(); printf("%d\n", c);
Est égal à: p>
int a = 1; int b = 2; int c = [a, b]{ return a + b; }
Lambda peut être converti en std :: function et appelé à tout moment ... Par exemple: https://wandbox.org/permlink/1RQUF3UUrsgRdJ42
Cela ne compile pas. Vous stockez le résultat, pas le foncteur réel. Et puisque la question est de savoir comment stocker au mieux les choses ... (bien qu'un template
avec un CallableType
stocké que vous pouvez initialiser depuis un lambda est une bonne idée! Frappez-moi quand vous avez fait les corrections ...)
Dans l'exemple source, lambda n'est également stocké nulle part :) Voici ce que je veux dire: wandbox.org/permlink/SOVKItI2VNNA5FK6 < / a>
Hm peut-être que je décris pas bien. L'idée principale est que si vous devez appeler une fonction avec un nombre variable de paramètres, vous pouvez utiliser une fonction initialisée à partir de lambda pour cela.
int main() { Class test(add, 1, 2); test.function(); }
La réponse acceptée n'est pas bonne car std :: bind
peut être totalement remplacé par lambda, l'utilisation de std :: bind
est démodée dans le c ++ moderne et peut causer un problème de performances car la construction de std :: function
est assez lourde.
[voir Pourquoi utiliser std :: bind sur lambdas en C ++ 14? ]
Permettez-moi de vous présenter c ++ 20 std :: bind_front
, cette fonction est destinée à remplacer std::bind
:
int add(int i, int j, int k) { return i + j + k; } auto f1 = std::bind_front(add, 1); auto f2 = std::bind_front(add, 1, 2); auto f3 = std::bind_front(add, 1, 2, 3); // all will print '6' std::cout << f1(2, 3) << '\n'; std::cout << f2(3) << '\n'; std::cout << f3() << '\n';
Et n'oubliez pas d'inclure l'en-tête
:)
Que comptez-vous faire de cette collection de fonctions? Comment comptez-vous les appeler?
Tout comme je l'ai démontré dans la fonction principale.
@ user686368 Ce n'est pas vraiment ce que vous voulez. Nous essayons de déterminer si vous n'essayez pas réellement de résoudre un problème XY ici.
@ user686368 Je voulais dire plus généralement. Si vous aviez vraiment besoin d'appeler une seule fonction une seule fois, vous écrivez simplement
add (1, 2)
. Vous devez expliquer vos contraintes.Ceci est juste un exemple simple pour illustrer ma question. La classe aurait beaucoup plus de fonctionnalités et la fonction n'en serait qu'une facette.
Oui, et je demande quelle est cette fonctionnalité.
La classe sera un bouton et la fonction sera utilisée lorsque vous appuyez sur le bouton. Les autres fonctionnalités seront des choses comme la position, la profondeur, le cadre et d'autres choses comme ça.
D'accord, oui, c'est une bonne approche alors.