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.
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 |
---|---|---|
|
|
|
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();
}
Quelques liens pour en savoir plus.