Déduire la taille d'un tableau par généricité

La tradition C veut que passer un tableau en paramètre à une fonction implique passer à la fois l'adresse de son premier élément et sa taille :

#include <iostream>
#include <cstring>
using namespace std;
template <class T>
   void f(const T *tab, size_t n)
   {
      cout << n << " éléments:\n\t";
      for (size_t i= 0; i < n; ++i)
         cout << tab[i] << ' ';
   }
int main()
{
   short t0[5] = { 2,3,5,7,11 };
   float t1[5] = { 1.5f, 2.5f, 3.5f, 4.5f, 5.5f };
   f(t0);
   f(t1);
   char s[] = "J'aime mon prof!";
   f(s, strlen(s));
}

En C++, nous pourrions être tentés d'alléger, lorsque cela s'avère possible, l'écriture de la manière suivante :

#include <iostream>
using namespace std;
template <class T, size_t N>
   int f(T tab[N]) {
      cout << N << " éléments:\n\t";
      for (size_t i= 0; i < N; ++i)
         cout << tab[i] << ' ';
   }
int main() {
   short t0[5] = { 2,3,5,7,11 };
   float t1[5] = { 1.5f, 2.5f, 3.5f, 4.5f, 5.5f };
   f(t0);
   f(t1);
   char s[] = "J'aime mon prof!";
   f(s);
}

Cela ne fonctionnera malheureusement pas puisque le tableau tab défini dans le programme principal vivra ce qu'on nomme la décrépitude de pointeurs (Pointer Decay) lors de l'appel à la fonction f(). À ce stade, le tableau sera vu comme un simple pointeur et le moteur ne pourra plus en déduire la valeur de N (plus encore : le code ne compilera pas!).

Toutefois, exprimé comme suit :

#include <iostream>
using namespace std;
template <class T, size_t N>
   void f(T (&tab)[N]) {
      cout << N << " éléments:\n\t";
      for (size_t i= 0; i < N; ++i)
         cout << tab[i] << ' ';
   }
int main() {
   short t0[5] = {2 ,3,5,7,11 };
   float t1[5] = { 1.5f, 2.5f, 3.5f, 4.5f, 5.5f };
   f(t0);
   f(t1);
   char s[] = "J'aime mon prof!";
   f(s);
}

...la valeur de N persistera et le code compilera adéquatement, puis affichera les valeurs attendues. Le passage par référence du tableau suffit à prévenir la décrépitude de pointeur sur le tableau d'origine.

Manières de calculer la taille d'un tableau

Soit un tableau tel que celui-ci :

int main()
{
   int tab[] = { 2,3,5,7,11 }; // équivaut à int tab[5] = { 2,3,5,7,11 };
}

Si nous souhaitons connaître la taille du tableau, il est possible de l'évaluer comme suit :

int main()
{
   int tab[] = { 2,3,5,7,11 }; // équivaut à int tab[5] = { 2,3,5,7,11 };
   enum { N = sizeof(tab) / sizeof(tab[0] }; // <-- si sizeof(int)==4, alors N == (5 * 4) / 4 donc N == 5
}

Si nous souhaitons appeler une fonction en lui passant le tableau et sa taille en paramètres, nous avons deux grandes options :

Un exemple de chacune de ces options suit.

Pointeur et taille, non-générique Pointeur et taille, générique Tableau, générique
#include <iostream>
using namespace std;
void afficher(const int *tab, size_t n) {
   for (size_t i = 0; i < n; ++i)
      cout << tab[i] << ' ';
   cout << endl;
}
int main() {
   int tab[] = { 2,3,5,7,11 };
   enum { N = sizeof(tab) / sizeof(tab[0] };
   afficher(tab, N);
}
#include <iostream>
using namespace std;
template <class T>
   void afficher(const T *tab, size_t n) {
      for (size_t i = 0; i < n; ++i)
         cout << tab[i] << ' ';
      cout << endl;
   }
int main() {
   int tab[] = { 2,3,5,7,11 };
   enum { N = sizeof(tab) / sizeof(tab[0] };
   afficher(tab, N);
}
#include <iostream>
using namespace std;
template <class T, size_t N>
   void afficher(const T (&tab)[N]) {
      for (size_t i = 0; i < N; ++i)
         cout << tab[i] << ' ';
      cout << endl;
   }
int main() {
   int tab[] = { 2,3,5,7,11 };
   afficher(tab);
}

Si vous souhaitez simplement une fonction size(C) retournant le nombre d'éléments d'un certain conteneur de type C, une implémentation possible serait la suivante.

template <class T, size_t N>
     auto size(const T (&arr)[N]) {
        return N;
     }
  template <class C>
     auto size(const C &conteneur) {
        return conteneur.size();
     }

Lectures complémentaires

Quelques liens pour en savoir plus.


Valid XHTML 1.0 Transitional

CSS Valide !