IdentifiantMot de passe
Loading...
Mot de passe oublié ?Je m'inscris ! (gratuit)
Navigation

Inscrivez-vous gratuitement
pour pouvoir participer, suivre les réponses en temps réel, voter pour les messages, poser vos propres questions et recevoir la newsletter

C++ Discussion :

template class et conversion


Sujet :

C++

Vue hybride

Message précédent Message précédent   Message suivant Message suivant
  1. #1
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2011
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 149
    Par défaut template class et conversion
    Bonjour,

    voici la partie de mon code où j'ai un petit soucis :
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    template<class T, const int N>
    class CVecteur
    {
    	private:
    			int size;
    			T data[N];
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    template<class T, const int N>
    CVecteur<T,N> :: CVecteur()
    {
    	size = 0;
    	T valeur = NULL;
    	cout<<"\nCREATION DE L'OBJET\nEntrer -1 pour arreter de remplir le tableau\n";
    	while ((valeur != -1)&&(size < N))
    	{
    		cout <<"Nombre ? ";
    		cin>>valeur;
    		if (valeur!=-1)
    			{
    				data[size]=valeur;
    				cout <<"data[" <<size<<"] = "<< data[size] << endl;
    				size++;
    			}
    	}
    }
    Dans ce constructeur de class, je remplie une tableau de type T et de taille maxi N. L'utilisateur entre des valeurs, et lorsqu'il entre -1 le remplissage du tableau s'arrête. Jusque là je pense avoir réalisé le travail demandé.

    Lorsque T est un int par exemple, aucun problème. Par contre, j'ai essayé avec T = char et là, soucis : lorsque je rentre -1 pour arrêter de remplir le tableau, le caractère - va dans une case et 1 dans la suivante, et la condition (valeur!=-1) est considéré comme fausse...

    Je viens donc vers vous pour vous demander un moyen de contourner ce soucis...

    Comme d'habitude, toutes remarques sont les biens venues ;-)

  2. #2
    Membre Expert
    Avatar de imperio
    Homme Profil pro
    Étudiant
    Inscrit en
    Mai 2010
    Messages
    872
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France, Ain (Rhône Alpes)

    Informations professionnelles :
    Activité : Étudiant
    Secteur : High Tech - Éditeur de logiciels

    Informations forums :
    Inscription : Mai 2010
    Messages : 872
    Par défaut
    Recupere toujours ta valeur dans un int. Apres copie cette valeur dans ta variable valeur si tu veux mais je ne pense pas que ce soit utile... Pour bien comprendre ce qu'il se passe, '-' et '1' sont 2 char donc cin te les retournera tous les deux. Par-contre avec un int il te retournera -1.

  3. #3
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2011
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 149
    Par défaut
    Je comprends bien que quand T est un char, -1 est considéré comme 2 caractères. Pour y remédier je penser créé une variable int qui stockerait la valeur entrée par l'utilisateur.
    Voilà ce que j'ai essayé de faire :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    template<class T, const int N>
    CVecteur<T,N> :: CVecteur()
    {
    	size = 0;
    	T valeur = NULL;
    	int val = NULL;
    	cout<<"\nCREATION DE L'OBJET\nEntrer -1 pour arreter de remplir le tableau\n";
    	while ((val != -1)&&(size < N))
    	{
    		cout <<"Nombre ? ";
    		cin>>val;
    		valeur = val;
    		if (val!=-1)
    			{
    				data[size]=valeur;
    				cout <<"data[" <<size<<"] = "<< data[size] << endl;
    				size++;
    			}
    	}
    }
    Lorsque j'entre -1, ça marche en effet, par contre lorsque j'entre 'm' par exemple, tout mon programme s'exécute d'une traite... Je vois pas trop comment m'en sortir, sachant que valeur sera de type T. Sinon je me demandé s'il était possible de stocker une valeur depuis cin >> vers 2 variables??

  4. #4
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Salut,

    Le fait est qu'il ne faut pas confondre un caractère particulier avec la valeur qui le représente.

    le type char est un type dit primitif car il est composé exclusivement d'un certain nombre (au risque de prendre un raccourci honteux, disons 8, car c'est souvent le cas) de bits "mis ensemble".

    En tant que tel, il est tout à fait possible d'utiliser le type char pour représenter des valeurs comprises dans un intervalle donné, en fonction de la signification accordée au bit de poids fort. Ces valeurs sont comprises dans l'intervalle [ 0, 256[ si l'on considère la version non signée du type char et dans l'intervalle [-127, 128[ si l'on considère sa version signée.

    Le problème, c'est que le type char est un type primitif particulier : il est mis en relation avec une table d'équivalence vers des "glyphes" (comme les symboles que l'on trace pour représenter le chiffre "1" ou la lettre "Z") que l'on peut (ou non) afficher et / ou introduire au travers de différents éléments.

    Et les choses commencent donc à se compliquer parce qu'il est assez facile de tomber dans le piège dans lequel tu es toi même tombé : la valeur numérique d'un élément de type char correspond à l'index du symbole correspondant dans la table d'équivalence.

    Par exemple, la table de caractères utilisée à l'origine est la table de caractères ASCII, qui définit un glyphe équivalent pour les valeurs comprises entre 0 et 127.

    Il y a d'autres tables de caractères, mais le principe reste toujours le même : la valeur "numérique" d'un caractère correspond systématiquement à l'index du glyphe qui permet de représenter le symbole en question.

    Autrement dit, le glyphe "1" ne correspond pas forcément (il ne le fait d'ailleurs jamais) à la valeur numérique 1: le glyphe "1" se trouve en réalité à l'index 49 de la table de caractères ASCII, ce qui fait que la valeur numérique associée au glyphe "1" est en réalité 49 .

    Et pour compliquer le tout, on se rend compte que la table de caractères ASCII est en fait divisée en trois sections relativement distinctes:

    • Les valeurs comprises entre 0 et 31 inclus représentent des caractères "non imprimables" qui étaient à l'origine utilisés par les telex et autres appareils pour leur permettre de communiquer entre eux (certains sont d'ailleurs encore utilisés aujourd'hui )
    • Les valeurs comprises entre 32 et 127 correspondent à des glyphes clairement définis, dont la représentation est commune à l'ensemble des langues qui utilisent notre alphabet.
    • Les valeurs comprises entre 128 et 254 correspondent à des glyphes spécifiques à différentes langues.


    Hein quoi Ah ben oui, il manque la valeur 255, qui est elle aussi clairement déterminée et qui correspond à une caractère que l'on a "effacé"

    Ca, c'est un reliquat du temps où les pionniers utilisaient les cartes perforées pour introduire leurs données : s'ils avaient fait une erreur en perforant leur carte, ils pouvaient "effacer" le caractère introduit en mettant tous les bits à 1

    Au niveau de l'utilisateur, on peut en outre définir un certain nombre de "sous ensembles" au niveau des glyphes auxquels il sera confronté:
    • les glyphes qui permettent de représenter des valeurs numériques, et ce dans différentes bases. Il y a, bien sur, les glyphes 0, 1, 2, 3, 4, 5, 6, 7, 8 et 9, mais on peut également envisager les glyphes a, A, b, B, c, C, d, D, e, E, f et F si on envisage une représentation hexadécimale de la valeur.
    • les glyphes qui permettent de représenter des lettres, aussi bien en majuscule qu'en minuscule ( a ... z et A ... Z )
    • Les glyphes qui permettent de représenter une séparation entre deux chaines de caractères : l'espace bien sur, mais aussi la tabulation et le retour à la ligne (pour ne citer que les principaux)
    • Et enfin, les glyphes qui n'entrent dans aucune de ces catégories, comme &, @, ! et tous les autres.


    Les choses deviennent souvent encore plus confuses du fait que, du point de vue de l'utilisateur, ce sont systématiquement les glyphes qui prennent de l'importance, alors que, du point de vue de la "mécanique interne" de l'ordinateur, c'est bel et bien la valeur numérique qui a de l'importance.

    En effet, lorsque tu lit la valeur "125" à l'écran, ou lorsque tu introduis cette valeur au travers de ton clavier, tu vois apparaitre (ou tu introduis) une chaine de caractères, composée des glyphes qui te permettent de fournir une représentation de la valeur en question que tu sois en mesure de comprendre.

    Cela implique qu'il faut un "mécanisme de conversion" qui soit capable de faire le lien entre la valeur telle qu'elle est visible pour l'utilisateur (ou telle qu'elle a été introduite par lui) et la valeur telle qu'elle sera manipulée par l'ordinateur et inversement.

    Ces mécanismes de conversions sont intégrés aux flux formatés grâce aux différentes surcharges qui existent pour les opérateurs << et >>.

    Le problème de l'opérateur >> (car c'est de lui qu'il s'agit spécifiquement), c'est qu'il va, par défaut, considérer que les seuls glyphes qu'il peut utiliser pour convertir une chaine de caractères introduite par l'utilisateur en valeur numérique sont les glypes 0, 1, 2, 3, 4, 5, 6, 7, 8 et 9 (et, éventuellement le glyphe -, s'il se trouve à gauche du premier glyphe représentant un chiffre et qu'il y est accolé).

    En effet, cet opérateur va s'arrêter au premier caractère qui n'est pas convertible en valeur numérique:

    Ainsi, si tu as un code (tout simple) proche de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    int main(){
        int value;
        std::cout<<"introduisez une valeur numérique :";
        std::cin>>value;
        std::cout<<"got value "<<value<<std::endl;
        return 0;
    }
    l'introduction de n'importe quelle valeur numérique susceptible d'entrer dans l'intervalle autorisée pour le type int te permettra d'obtenir un résultat correct, mais l'introduction de m ou de maman affichera "0" comme résultat.

    Et les choses se compliqueront encore d'avantage du fait que le premier caractère qui n'est pas convertible en valeur numérique reste dans le "buffer" d'entrée.

    Autrement dit, si on modifie un tout petit peu le code que je viens de montrer en
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    int main(){
        int value = 0;
        while(value!=-1){
            std::cout<<"introduisez une valeur numérique :";
            std::cin>>value;
            std::cout<<"got value "<<value<<std::endl;
        }
        return 0;
    }
    Et que j'introduis successivement les valeurs 158, -3587, 4587, 123 -987 et -1, cela va fonctionner à merveile : le programme s'arrêtera une fois que j'aurai introduit "-1".

    Par contre, si je me trompes dans mon introduction et que j'introduis 158, -3587, 4587, m, le programme estimera que j'ai introduit la valeur 0 (comme en témoignera l'affichage), mais ne supprimera pas le caractère m du buffer.

    Du coup, il retournera dans la boucle (vu que value n'est pas égal à -1), et essayera de nouveau de lire ce que n'ai introduit... c'est à dire, la lettre m (vu qu'il ne l'a pas supprimée), ce qui fera que value sera de nouveau évalué à 0.

    Bref, nous entrerons dans une boucle "infinie" dont seule "une autre condition" sera en mesure de nous faire sortir (à condition qu'elle soit un jour atteinte).

    Ton code présente une telle condition : (size < N) qui sera, effectivement atteinte à un moment ou à un autre, et c'est ce qui explique que tu te rendes compte au final que, si tu introduit m, le programme poursuit automatiquement son exécution après la boucle.

    Tu dois donc introduire dans ta logique le fait que l'utilisateur est, définitivement, un imbécile distrait, qui, s'il a la moindre chance de faire une connerie la fera systématiquement à un moment ou à un autre. C'est, du moins, ce que nous apprend la loi (empirique) de Murphy

    Par chance, les développeur de la bibliothèque standard ont prévu le coup : les flux sont implicitement convertibles en un booléen qui permet d'indiquer s'il se trouve dans un état cohérent ou non.

    On pourrait donc éviter le problème de l'utilisateur distrait en modifiant une dernière fois le code sous la forme de

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    int main(){
        int value = 0;
        while(value!=-1){
            if(std::cin>>value){
                /* on a bel et bien obtenu une valeur numérique */
                std::cout<<"got value "<<value<<std::endl;
            }else{
                std::cout << "j'ai demande une valeur NUMERIQUE!!! essayez encore"
                          << std::endl;
                /* avant d'aller plus loin, il faut purger le buffer et réinitaliser le flux
                 * pour être sur que l'introduction suivante ait une chance d'être acceptée
                 */
     
                std::cin.clear(); 
                std::cin.ignore( std::numeric_limits<std::streamsize>::max(), '\n' );
            }
        }
        return 0;
    }
    De cette manière, si l'utilisateur introduit les valeurs 158, -3587, 4587, 123, m, il se fera insulter au message de
    j'ai demande une valeur NUMERIQUE!!! essayez encore
    et le programme pourra continuer par la suite
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  5. #5
    Membre confirmé
    Profil pro
    Inscrit en
    Octobre 2011
    Messages
    149
    Détails du profil
    Informations personnelles :
    Localisation : France

    Informations forums :
    Inscription : Octobre 2011
    Messages : 149
    Par défaut
    Merci beaucoup pour ton aide, ça m'a grandement aidé!

    Du coup j'ai essayé ceci :

    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    template<class T, const int N>
    CVecteur<T,N> :: CVecteur()
    {
    	size = 0;
    	int val = NULL;
    	T valeur = NULL;
    	cout<<"\nCREATION DE L'OBJET\nEntrer -1 pour arreter de remplir le tableau\n";
    	while ((val != -1)&&(size < N))
    	{
    		cout <<"Nombre ? ";
    		if ((cin>>val)&&(cin>>valeur))
    		{
    			if (val!=-1)
    				{
    					data[size]=valeur;
    					cout <<"data[" <<size<<"] = "<< data[size] << endl;
    					size++;
    				}
    		}
    		else
    		{
    			cin.clear(); 
                cin.ignore(numeric_limits<streamsize>::max(), '\n' );
    		}
    	}
    }
    Le problème est que lorsque je lance le débogage sous Visual Studio 2012 ça me marque :
    error C2143: erreur de syntaxe*: absence de ')' avant '::'
    error C2589: '('*: jeton non conforme à droite de '::'
    warning C4003: nombre de paramètres réels
    Je ne comprends pas le pourquoi du problème ^^'

    Et le fait de faire est-il possible?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ((cin>>val)&&(cin>>valeur))
    Je voudrais en effet stocker le cin dans 2 variables

  6. #6
    Expert éminent
    Avatar de koala01
    Homme Profil pro
    aucun
    Inscrit en
    Octobre 2004
    Messages
    11 644
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Âge : 53
    Localisation : Belgique

    Informations professionnelles :
    Activité : aucun

    Informations forums :
    Inscription : Octobre 2004
    Messages : 11 644
    Par défaut
    Oups, j'ai oublié de préciser:

    Pour pouvoir utiliser std::limits (dans std::cin::ignore), tu dois inclure le fichier d'en-têtes <limits>.

    Autrement, ca ne fonctionnera pas
    Et le fait de faire est-il possible?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ((cin>>val)&&(cin>>valeur))
    Cela peut parfaitement fonctionner, mais fais attention à la logique que tu mets en place de la sorte:

    Tu utilises le AND "optimisé" dans ce code, ce qui fait que tu n'essayeras de récupérer la valeur de la variable valeur que si tu as bel et bien réussi à récupérer la valeur de val.

    Généralement si on veut récupérer plusieurs valeurs, on le fera plutôt sous la forme de
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    std::cin>> val>> valeur;
    A méditer: La solution la plus simple est toujours la moins compliquée
    Ce qui se conçoit bien s'énonce clairement, et les mots pour le dire vous viennent aisément. Nicolas Boileau
    Compiler Gcc sous windows avec MinGW
    Coder efficacement en C++ : dans les bacs le 17 février 2014
    mon tout nouveau blog

  7. #7
    Membre Expert
    Homme Profil pro
    Étudiant
    Inscrit en
    Juin 2012
    Messages
    1 711
    Détails du profil
    Informations personnelles :
    Sexe : Homme
    Localisation : France

    Informations professionnelles :
    Activité : Étudiant

    Informations forums :
    Inscription : Juin 2012
    Messages : 1 711
    Par défaut
    Attention aux initialisation à NULL, ça marche souvent car souvent NULL n'est autre qu'un define (void*)0, mais c'est pas portable.

    Citation Envoyé par oieretxe Voir le message
    Et le fait de faire est-il possible?
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    if ((cin>>val)&&(cin>>valeur))
    Je voudrais en effet stocker le cin dans 2 variables
    Tu lis 2 valeurs, je pense que ce que tu veux faire, c'est lire une valeur et si elle est correcte la copier dans valeur, ce qui se fait
    Code : Sélectionner tout - Visualiser dans une fenêtre à part
    1
    2
    3
    4
    if (cin>>val) {
       valeur = static_cast<T>(val);
       ...
    }
    Attention toutefois, en passant par un int, tu limite le type T à un type représentable dans un int.

    (Sinon pas d'erreur de compilation chez moi, avec VS2012)
    On peut avoir la ligne des erreurs ?

  8. #8
    Invité
    Invité(e)
    Par défaut
    Bonjour,

    Pourquoi ne pas simplement saisir une ligne "vide" ?
    À la place de saisir une valeur quelconque qui aurait des risques d'être invalide dans tel ou tel type, juste ne rien saisir ?
    On pourrait penser à saisir dans tous les cas un std::string, vérifier si celui-ci est vide ou non, et si non effectuer la conversion en suivant ce principe

Discussions similaires

  1. Réponses: 19
    Dernier message: 23/12/2009, 19h22
  2. Réponses: 3
    Dernier message: 09/04/2009, 11h30
  3. Template / Classe Latex
    Par zifox dans le forum Débuter
    Réponses: 3
    Dernier message: 26/03/2009, 18h04
  4. template<class> et template<typename>
    Par mister3957 dans le forum C++
    Réponses: 10
    Dernier message: 01/11/2007, 09h32
  5. Héritage classe template->classe template
    Par zabibof dans le forum Langage
    Réponses: 5
    Dernier message: 11/08/2007, 11h05

Partager

Partager
  • Envoyer la discussion sur Viadeo
  • Envoyer la discussion sur Twitter
  • Envoyer la discussion sur Google
  • Envoyer la discussion sur Facebook
  • Envoyer la discussion sur Digg
  • Envoyer la discussion sur Delicious
  • Envoyer la discussion sur MySpace
  • Envoyer la discussion sur Yahoo