Question programmation avec Template

Bonjour,

Dans l'apprentissage du c/c++, je me pose des questions sur les templates, par exemple, si je prends le code dans la seconde balise.

  1. on déclare bien le template en premier?

  2. dans le cas où j'effectue l'appel pressKeypad(c) avec c='0', je vais passer par le template, suis je obliger de déclarer les deux façons de faire const char et char?

Je me demande quelle est la bonne méthode
Faut t'il faire ainsi, parce que ça devient lourd à gérer je trouve:

Merci pour toute l'aide que vous pourrez m'apporter.

    size_t pressKeypad(char& c){ 
      return pressKeypad((const char) c);
    }
      
    size_t releaseKeypad(char& c){
      return releaseKeypad((const char) c);
    }
    template <typename T>
    size_t pressKeypad(T & v)
    {
      if (v < 0 || (v > 9 && v != 13)) {
        return 0;
      }
      return pressReleaseNum_((const uint8_t&)v, _Press | _For_keypad);
    }

    template <typename T>
    size_t releaseKeypad(T & v)
    {
      if (v < 0 || (v > 9 && v != 13)) {
        return 0;
      }
      return pressReleaseNum_((const uint8_t&)v, _Release | _For_keypad);
    }

    size_t pressKeypad(const char& c);
    size_t releaseKeypad(const char& c);

Bonjour,

Tu ne redeclares pas les fonctions. Tu utilises simplement la fonction définie avec le template.
C'est justement un des intérêts des templates par rapport à la redéfinition des fonctions avec des arguments différents.

Oui mais le template dans mon cas ne doit servir que pour les valeurs numériques.

Donc le template n'est pas justifié. Une fonction inline (voire une fonction 'classique') serait plus justifiée.

Merci Kamill pour votre aide, j'aurais certainement encore besoin de vous pour un autre problème mais faut que je fasse quelques tests avant.

J'ai remarqué la chose suivante ci dessous, du coup j'ai rajouté le mot clé const dans la fonction du template et je n'ai plus le souci.

// ici la surcharge n'est pas possible

size_t test(const char c)
{
  Serial.println("Fonction const char");
}

size_t test(char c)  
{
  Serial.println("Fonction char");
}
// mais là pas de problème

size_t test(const char& c)
  Serial.println("Fonction const char&");
}

size_t test(char& c) 
{
  Serial.println("Fonction char&");
}

Un autre souci que j'ai remarqué, je tente de faire ce qui n'est pas permis et ce qui n'est pas dans la logique du C.

// Dans le cas ou je souhaiterais traiter que des nombres de 0 à 10 par exemple
// je pensais que je pouvais faire deux procédures pour traiter tous les types de variable numériques
// mais apparemment c'est pas comme ça que les choses sont prévues !!!

size_t test(long num)
{
  Serial.println("Fonction long num");
}

size_t test(unsigned long num)
{
  Serial.println("Fonction unsigned long num");
}


void setup() {
  // put your setup code here, to run once:
  Serial.begin(115200);
  while (!Serial) {
    ; // wait for serial port to connect. Needed for native USB
  }
}

void loop() {
  // put your main code here, to run repeatedly:

  long numero1 = 111;
  test(numero1);

  unsigned long numero2 = 222;
  test(numero2);

// overloaded pour les deux appels qui suivent
  int numero3 = 333;
  test(numero3);

  unsigned int numero4 = 444;
  test(numero4);
  
  while (1);
}

const n'a d'utilité pour le paramètre d'une fonction que si le paramètre est passé par référence (explicite ou implicite).
Donc
size_t test(const char c)
et
size_t test(char c)
c'est la même chose. C'est pour ça que le compilateur ne veut pas les deux définitions

Par contre dans le cas du passage par valeurs les deux fonctions ne font pas forcément la même chose
size_t test(const char& c) ne peut pas modifier c (en fait la variable passée en paramètre)
size_t test(char& c) peut modifier c

Pour ce qui est de ton deuxième message, le compilateur n'aime pas ce qui peut provoquer des ambiguïtés.

Cependant dans ces cas particuliers je pense qu'il ne devrait pas avoir d'erreur car l'int devrait être promu en long et le unsigned int promu en unsigned long.

Par contre le compilateur devrait signaler des erreurs pour les deux fonctions sensées retourner un size_t et qui ne retournent rien.

Ah d'accord, donc si je reprends, dans le premier cas, j'ai une copie de la variable que je déclare en const ou pas mais je dois choisir alors que dans le second, j'ai un alias donc en lien direct avec la variable et je peux dans ce cas dissocier. C'est beaucoup plus clair maintenant.

Il me semble que pour le dernier post, cela ne compile pas mais je referais un test (je suis sur tablette).

Merci, c'est moins obscur maintenant.

kamill:
const n'a d'utilité pour le paramètre d'une fonction que si le paramètre est passé par référence (explicite ou implicite).

Pas seulement.
Il empêche que le paramètre soit modifié dans la fonction.
J'ai fait 15 ans d'informatique sécuritaire, et c'était une obligation de déclarer les paramètres d'une fonction avec const
(les paramètres reçus en arguments ne bougent pas pendant la fonction).

15 ans à appliquer une obligation douteuse.
Quel est l’inconvénient à modifier un paramètre transmis par valeur?

Après différentes recherches, il semble qu'il faille distinguer promotion et conversion, c'est la raison pour laquelle on ne peut pas créer deux procédures identiques avec unsigned long et long, il y a ambiguïté car il y aurait promotion jusqu'au int seulement si je ne dis pas de bêtises.

Et effectivement, on peut créer deux procédures test(...) avec unsigned int et int et là ça marche,et dans ce cas c'est int sera privilégiée.

C'était pour conclure le fil, je me doute bien que c'est pas une surprise pour ceux qui pratique ce langage.

kamill:
15 ans à appliquer une obligation douteuse.
Quel est l’inconvénient à modifier un paramètre transmis par valeur?

Il n'y a aucun doute.

Quand tu fais de l'informatique sécuritaire (piloter un réacteur nucléaire, un métro, une fusée Ariane...), il va falloir convaincre un tas de gens, qu'on appele des décideurs, que ton logiciel est sûr.
Or un décideur ne comprend rien en informatique. La seule chose qu'on puisse faire, c'est le rassurer. C'est pour ça (et pas grand chose d'autre) qu'on a inventé l'Assurance Qualité, les normes ISO 9001, etc.
Pour le rassurer, donc, il faut donc empiler une liste considérable de petites choses qui, si elle ne peuvent prouver que le logiciel est sûr, contribuent à lui attribuer une bonne confiance.
Pour le cas qui nous intéresse, regarde le code suivant :

int une_fonction ( int nb_cas )
{
  while ( --nb_cas )
    faire_une _truc ( nb_cas );
  // ici nb_cas vaut 0

  // plein de code ...
  // plein de code ...
  // plein de code ...

  for ( int i=0, i< nb_cas; i++ )
    faire_autre_cosse ( i );
}

Arrivé en fin de fonction (qui peut être un peu longue), le programmeur a oublié que nb_cas avait été modifié. Pour lui cette variable, bien nommée, représente toujours le nombre de cas. Malheureusement il y a un bug ( faire_autre_chose() n'est jamais appelé).
Oui, bon, c'est un bug basique, sera détecté aux tests... mmmoouuii ?
Sauf que dans ce monde, on aime pas les bugs. Mais vraiment pas (tu prends le métro ? Tu n'aimerais pas que la porte s'ouvre pendant la marche)
Alors on fait tout ce qu'on peut pour les éviter, même si ça paraît très con. Comme déclarer const nb_cas., ce qui évite ce genre de bug, qui peut être assez générique.

Mouais...
C'est la même chose pour n'importe quelle variable. Si tu oublies ce que tu as fait avec, il y a de fortes chances que ça provoque des bugs.

kamill:
Si tu oublies ce que tu as fait avec

Certes ... mais ça arrive. Le programmeur parfait qui n'oublie jamais rien, ça n'existe pas. Alors on mets des garde-fous partout. La liste est longue (notation hongroise pour les variables, les boucles for autorisées seulement avec des bornes constantes, ... )
Si tu montes au 1er étage de la tour Eiffel, il y a une barrière au bord de la plateforme. Pourtant, tu n'es pas fou, tu ne vas pas sauter ! Mais je suis sûr que tu l'aimes bien quand même, cette barrière. :slight_smile:

C'était juste pour préciser à quoi peut servir const...