La fonction passée comme argument de modèle
Je suis à la recherche pour les règles impliquant passant modèles C++ des fonctions comme arguments.
C'est pris en charge par le C++, comme illustré par un exemple ici:
#include <iostream>
void add1(int &v)
{
v+=1;
}
void add2(int &v)
{
v+=2;
}
template <void (*T)(int &)>
void doOperation()
{
int temp=0;
T(temp);
std::cout << "Result is " << temp << std::endl;
}
int main()
{
doOperation<add1>();
doOperation<add2>();
}
L'apprentissage de cette technique est difficile, cependant. Googler pour "fonctionner comme un argument de modèle" n'avance pas beaucoup. Et le classique Les Modèles C++ Le Guide Complet étonnamment aussi ne discutent pas (au moins pas de ma recherche).
Les questions que j'ai est de savoir si c'est valable en C++ (ou juste une partie largement pris en charge extension).
Aussi, est-il un moyen pour permettre à un foncteur avec la même signature pour être utilisé de façon interchangeable avec des fonctions explicites lors de ce type de modèle invocation?
Ce qui suit ne pas de travail dans le programme ci-dessus, au moins dans Visual C++, car la syntaxe est évidemment faux. Il serait agréable d'être en mesure de passer d'une fonction pour un foncteur, et vice-versa, semblable à la façon dont vous pouvez passer un pointeur de fonction ou de functor pour le std::algorithme de tri si vous souhaitez définir une comparaison personnalisée de l'opération.
struct add3 {
void operator() (int &v) {v+=3;}
};
...
doOperation<add3>();
Des pointeurs vers un lien web ou deux, ou une page dans les Modèles C++ livre serait appréciée!
- Quel est l'avantage d'une fonction comme argument de modèle? Ne serait pas le type de retour d'un type de modèle?
- Liés: un lambda aucune capture peut se désintégrer à un pointeur de fonction, et vous pouvez passer que comme un modèle de param en C++17. Clang compile ok, mais le gcc (8.2) a un bug et incorrecte de la rejette comme ayant "pas de lien" même avec
-std=gnu++17
. puis-je utiliser le résultat d'une C++17 captureless lambda constexpr opérateur de conversion comme un pointeur de fonction du modèle de non-type d'argument?.
Vous devez vous connecter pour publier un commentaire.
Oui, c'est valable.
Que pour le faire fonctionner avec les foncteurs ainsi, la solution habituelle est quelque chose comme ceci à la place:
qui peut maintenant être appelé comme:
Le problème avec cela est que si cela rend difficile pour le compilateur associé à l'appel à
add2
, puisque tout le compilateur sait, c'est que d'un pointeur de fonction typevoid (*)(int &)
est passé àdoOperation
. (Maisadd3
, étant un foncteur, peut être incorporé facilement. Ici, le compilateur sait qu'un objet de typeadd3
est passé à la fonction, ce qui signifie que la fonction d'appel estadd3::operator()
, et non pas seulement de certains inconnus pointeur de fonction.)MyFunc == &MyFunc == &&&&MyFunc == ***&MyFunc
etctemplate <typename F> void doOperation(F&& f) {/**/}
), afin de lier, par exemple, peut passer un bind-expression à la place de la liaison il?static
foncteurs? Disons quevoid operator() (int &v)
à l'intérieur destruct add3
, est unstatic
fonction. Puis-je écrire;doOperation(add3::operator());
et assurez-vous que le compilateur va être en mesure de l'inclure cette statique foncteur sans difficulté?Paramètres du modèle peut être paramétrée par le type (typename T) ou en valeur (int X).
Le "traditionnel" C++ façon de templating un morceau de code est d'utiliser un foncteur - qui est, le code est dans un objet, et l'objet donne donc le code type unique.
Lorsque l'on travaille avec des fonctions traditionnelles, cette technique ne fonctionne pas bien, parce qu'un changement dans le type n'indiquent pas d'un spécifiques la fonction plutôt il indique que la signature de nombreuses fonctions. Donc:
N'est pas l'équivalent du foncteur cas. Dans cet exemple, do_op est instancié pour tous les pointeurs de fonction dont la signature est de type int X (int, int). Le compilateur devrait être assez agressif pour pleinement inline ce cas. (Je ne l'exclut pas cependant, comme l'optimisation du compilateur a reçu assez avancé.)
Une façon de dire que ce code n'est pas tout à fait faire ce que nous voulons, c'est:
est toujours légale, et clairement, ce n'est pas insérée. Pour obtenir l'in-lining, nous avons besoin d'un modèle par valeur, donc la fonction est entièrement disponible dans le modèle.
Dans ce cas, chaque instancié version de do_op est instancié avec une fonction spécifique déjà disponibles. Ainsi, nous attendons le code pour do_op de regarder un peu comme "return a + b". (Programmeurs Lisp, arrêtez votre sourire!)
On peut confirmer que cela est plus proche de ce que nous voulons parce que c':
échoue à compiler. GCC dit: "erreur:" func_ptr " ne peut pas apparaître dans une constante de l'expression. En d'autres termes, je ne peux pas entièrement développez do_op parce que vous n'avez pas donné assez d'infos au compilateur de temps pour savoir ce que nos op.
Donc, si le deuxième exemple est vraiment bien inline notre op, et la première n'est pas, à quoi bon le modèle? Qu'est-ce que ça fait? La réponse est: type de contrainte. Ce riff sur le premier exemple de travail:
Que exemple travail! (Je ne dis pas que c'est bon, C++, mais...) Ce qui s'est passé est do_op a été basé sur un modèle autour de la signatures des différentes fonctions, et chaque instanciation écrira différents de contrainte de type code. Ainsi, le instancié code pour do_op avec fadd ressemble à quelque chose comme:
Par comparaison, nos en-cas de valeur nécessite une correspondance exacte avec les arguments de la fonction.
int c = do_op(4,5,func_ptr);
est "clairement pas arriver inline".Pointeurs de fonction peuvent être passés comme paramètres de modèle, et cela fait partie de la norme C++
. Cependant, dans le modèle, ils sont déclarés et utilisés comme des fonctions plutôt que de pointeur de fonction. Au modèle instanciation on passe l'adresse de la fonction plutôt que de simplement le nom.
Par exemple:
Si vous souhaitez passer un foncteur type comme un argument de modèle:
Plusieurs réponses passer un foncteur exemple comme argument:
Le plus proche que vous pouvez obtenir à cet aspect uniforme avec un argument de modèle est de définir
do_op
deux fois, une fois avec un non-type de paramètre et une fois avec un paramètre de type.Honnêtement, je vraiment attendus cet de ne pas compiler, mais cela a fonctionné pour moi avec gcc-4.8 et Visual Studio 2013.
Dans votre modèle
Le paramètre
T
est un non-type de paramètre du modèle. Cela signifie que le comportement de la fonction de modèle changements avec la valeur du paramètre (qui doit être fixée au moment de la compilation, ce qui le pointeur de la fonction des constantes).Si vous voulez quelque chose qui fonctionne à la fois en fonction des objets et des paramètres de la fonction vous avez besoin d'un tapé modèle. Lorsque vous faites cela, cependant, vous devez également fournir une instance de l'objet (soit la fonction de l'instance d'un objet ou d'un pointeur de fonction) à la fonction au moment de l'exécution.
Il y a quelques petites considérations de performance. Cette nouvelle version peut être moins efficace avec le pointeur de fonction des arguments que la fonction de pointeur n'est derefenced et appelé au moment de l'exécution alors que votre pointeur de fonction template peut être optimisé (éventuellement l'appel de la fonction inline) en fonction de pointeur de fonction utilisé. La fonction des objets peuvent souvent être très efficacement élargi avec l'tapé modèle, même si, comme le particulier
operator()
est complètement déterminé par le type de la fonction de l'objet.La raison de votre foncteur exemple ne fonctionne pas, c'est que vous avez besoin d'une instance d'invoquer la
operator()
.Edit: en Passant l'opérateur comme une référence ne fonctionne pas. Pour des raisons de simplicité, de le comprendre comme un pointeur de fonction. Il vous suffit d'envoyer le pointeur, pas une référence.
Je pense que vous essayez d'écrire quelque chose comme cela.
.
.