Bonjour,
Je voudrais definir une seul et même fonction acceptant le type d'un argument variable comme suit.
Est-ce possible et comment?Code:
1
2
3
4
5
6 std::string toto; double titi; my_func(toto); my_func(titi);
Merci de votre aide
Version imprimable
Bonjour,
Je voudrais definir une seul et même fonction acceptant le type d'un argument variable comme suit.
Est-ce possible et comment?Code:
1
2
3
4
5
6 std::string toto; double titi; my_func(toto); my_func(titi);
Merci de votre aide
Bonjour,
Les templates devraient pouvoir répondre à ce besoin, non ?
La FAQ dédiée : http://cpp.developpez.com/faq/cpp/?page=templatesCode:template<typename T> void my_func(T xx);
Cordialement,
A.
Si tu connais les type à l'avance, une simple surcharge peut suffire non ?
Le compilateur passe par une phase dite de "résolution de surcharge" dans laquelle il compare pour chaque fonction les types qu'on lui donne en argument et les types des différentes surcharges de la fonction qu'il possède. Il en déduit alors automatiquement la bonne surcharge à utiliser.Code:
1
2
3
4
5
6
7
8
9
10
11
12
13 void my_func(int i) { //... } void my_func(std::string s) { //.. } std::string s = "toto"; my_func(s);
Salult,
En l'occurrence, pour la std::string l'idéal serait de passer s sous la forme d'une référence (constante ou non, selon la possibilité que peut avoir la fonction de modifier la chaine d'origine)
Mais sinon, les deux réponses ci-dessus sont effectivement les deux solutions principales:
Soit, tu ne sais pas précisément quel type sera transmis à ta fonction, mais tu sais parfaitement ce qu'elle doit faire, quel que soit le type, et, dans ce cas, tu entre de plein pied dans le domaine de la programmation générique (bienvenue dans le merveilleux monde des template :D:D)
Soit tu connais précisément les différents types qui seront passés en paramètres, et tu peux appliquer "simplement" le principe de la surcharge de fonctions:
Tout ce qu'il faut, c'est qu'au moins un argument soit de type différent par rapport à l'ensemble des autres signatures possibles, et le type de retour n'intervient pas dans la résolution de la surcharge ;)
Ok merci pour vos réponses.
Au passage je ne sais pas ce qui ce passe mais je n'ai pas été averti par emails quelles étaient arrivées comme d'habitude!
Cela dit, je trouve la solution template bien appropriée et élégante mais j'ai tout de même une question.
Comment connaitre dans la focntion le type de donné envoyé en argument de maniére à pouvoir intégrer un traitement différent selon que la nature de l'argument.
Parce que si je regarde dans la Faq, je vois bien la méthode de spécialisation, mais ca resemble furieusement à l'autre solution consistant à ecrire une fonction par type. N'y a t-il pas moyen de connaitre le type d'une variable?
Dans ce cas là c'est peut être préférable d'utiliser une surcharge non?
Comme ça tu peux effectuer les traitements voulus pour chaque type de donnée séparément.
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13 void f(int i){ //... } void f(const std::string& s) { //... } f(10); f("toto");
Salut,
Si tu n'as pas besoin de connaître le type effectif des paramètres de la fonction, alors oui le template est la solution la plus pertinente.
Si tu as besoin de connaître le type effectif des paramètres, alors la surcharge comme proposée par Arzar est la plus pertinente.
Faire une fonction template en traitant des cas particulier de type est le début d'un long chemin d'incompréhension
Ok, je peux comprendre les sources d'erreurs d'une telle méthode, je serais tout de même interessé de savoir si il existe une moyen de connaitre le type d'une variable.
Ca dépend de ce que tu entends par là.
Selon le besoin, il y a les classes traits (mieux écrites et plus complètes que l'exemple suivant dans Boost) :
Et il y a typeinfo qui te donne aussi des informations :Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 #include <iostream> template<typename T> struct is_bool{static const bool value=false;}; template<> struct is_bool<bool>{static const bool value=true;}; template<class T> void dump() { std::cout<<std::boolalpha<<is_bool<T>::value<<std::endl; } int main() { dump<int>(); dump<bool>(); return 0; }
Sachant que typeid(T) retourne unCode:
1
2
3
4
5
6
7
8
9
10
11
12
13 #include <iostream> #include <typeinfo> template<class T> void dump() { std::cout<<typeid(T).name()<<std::endl; } int main() { dump<int>(); dump<bool>(); return 0; }
name n'est pas défini par la norme et est à la discrétion du compilateur. On se sert plutôt de type_info par comparaison :Code:
1
2
3
4
5
6
7
8
9
10
11 class type_info { public: virtual ~type_info(); bool operator== (const type_info& rhs) const; bool operator!= (const type_info& rhs) const; bool before (const type_info& rhs) const; const char* name() const; private: type_info (const type_info& rhs); type_info& operator= (const type_info& rhs); };
Code:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 #include <iostream> #include <typeinfo> template<class T> void dump() { if(typeid(T)==typeid(bool)){ std::cout<<"c'est un bool"<<std::endl; } if(typeid(T)!=typeid(bool)){ std::cout<<"ce n'est pas un bool"<<std::endl; } } int main() { dump<int>(); dump<bool>(); return 0; }
Ok merci beaucoup, c'est exactement ce que je cherchais
Ceci dit, il y n'y a pas de raison de considérer une classe ou une fonction générique (template) comme "universelle".
Comme les fonctions membres de classes template et les fonctions template déterminent un comportement particulier, il faut, quoi qu'il en soit, que le type réellement manipulé soit "compatible" avec le comportement à mettre en oeuvre.
Ainsi, si tu envisage une fonction, par exemple, add, qui effectue l'addition de deux éléments, il faut bien entendu que le type manipulé permette de faire cette addition ;)
L'avantage, c'est que, si tu essaye d'utiliser cette fonction sur un type qui ne supporte pas l'addition, tu aura un message d'erreur dés la compilation (parce que la résolution de type s'effectue lors de la compilation), ce qui te permettra au minimum de te poser la question de l'opportunité d'invoquer cette fonction avec le type en question ;)
L'alternative qui s'offre à toi est alors que
- Soit, il est opportun d'appeler cette fonction, mais il manque alors "quelque chose" (de quoi permettre cette addition, selon l'exemple) dans l'interface publique de ton type particulier
- Soit, il n'est pas opportun de l'appeler, et tu dois revoir ton raisonnement global
Tout à fait d'accord
Quid de boost::variant ?
Exemple et tutoriel ici
C'est bien sur une possibilité qu'il faut aborder, si cela a un sens...
Mais, de manière générale, je considère plus boost::variant comme la possibilité qu'une même variable puisse, à un moment donné, agir comme un type particulier et à un autre agir comme un type différent...
Ce peut donc être une solution, mais il me semble que ce n'est pas le problème exposé ici ;):D