62 votes

Quelqu'un peut-il expliquer ce code modèle qui me donne la taille d'un tableau ?

template<typename T, size_t n>
size_t array_size(const T (&)[n])
{
    return n;
}

Ce que je ne comprends pas, ce sont les paramètres de cette fonction modèle. Que se passe-t-il avec le tableau lorsque je le passe par là, ce qui donne n comme le nombre d'éléments du tableau ?

90voto

Johannes Schaub - litb Points 256113

Tout d'abord, vous devez comprendre qu'en essayant d'extraire une valeur d'un tableau, vous pouvez obtenir un pointeur sur son premier élément :

int a[] = {1, 2, 3};
int *ap = a; // a pointer, size is lost
int (&ar)[3] = a; // a reference to the array, size is not lost

Les références font référence à des objets en utilisant leur type exact ou leur type de base. La clé est que le modèle prend les tableaux par référence. Les tableaux (et non les références à ceux-ci) en tant que paramètres n'existent pas en C++. Si vous donnez à un paramètre un type de tableau, ce sera un pointeur à la place. L'utilisation d'une référence est donc nécessaire lorsque nous voulons connaître la taille du tableau passé. La taille et le type de l'élément sont automatiquement déduits, comme c'est généralement le cas pour les modèles de fonctions. Le modèle suivant

template<typename T, size_t n>
size_t array_size(const T (&)[n]) {
    return n;
}

Appelé avec notre tableau précédemment défini a instancie implicitement la fonction suivante :

size_t array_size(const int (&)[3]) {
    return 3;
}

Qui peut être utilisé comme ceci :

size_t size_of_a = array_size(a);

Il y a une variation que j'ai inventée il y a quelque temps. [Edit : il s'avère que quelqu'un a déjà eu cette même idée aquí ] qui peut déterminer une valeur au moment de la compilation. Au lieu de renvoyer la valeur directement, il donne au modèle un type de retour qui dépend de n :

template<typename T, size_t n>
char (& array_size(const T (&)[n]) )[n];

Vous dites que si le tableau a n le type de retour est une référence à un tableau de taille n et le type d'élément char . Maintenant, vous pouvez obtenir une taille déterminée au moment de la compilation pour le tableau passé :

size_t size_of_a = sizeof(array_size(a));

Parce qu'un tableau de char ayant n Les éléments ont une taille de n qui vous donnera également le nombre d'éléments dans le tableau donné. Au moment de la compilation, vous pouvez donc faire

int havingSameSize[sizeof(array_size(a))];

Comme la fonction n'est jamais appelée, elle n'a pas besoin d'être définie et n'a donc pas de corps. J'espère que j'ai pu clarifier un peu le problème.

21voto

Eclipse Points 27662

Pensez-y de cette façon, supposez que vous ayez un tas de fonctions :

// Note that you don't need to name the array, since you don't
// actually reference the parameter at all.
size_t array_size(const int (&)[1])
{
    return 1;
}

size_t array_size(const int (&)[2])
{
    return 2;
}

size_t array_size(const int (&)[3])
{
    return 3;
}
// etc...

Maintenant, quand tu appelles ça, quelle fonction est appelée ?

int a[2];
array_size(a);  

Maintenant, si vous templatisez l'arraysize, vous obtenez :

template <int n>
size_t array_size(const int (&)[n])
{
    return n;
}

Le compilateur va essayer d'instancier une version de array_size qui correspond au paramètre avec lequel vous l'appelez. Ainsi, si vous l'appelez avec un tableau de 10 ints, il instanciera array_size avec n=10.

Ensuite, il suffit de modéliser le type, de sorte que vous puissiez l'appeler avec autre chose que des tableaux d'int :

template <typename T, int n>
size_t array_size(const T (&)[n])
{
    return n;
}

Et vous avez terminé.

Editar : Une note sur le (&)

Les parenthèses sont nécessaires autour du & pour différencier les références à un tableau d'int (illégal) et les références à un tableau d'ints (ce que vous voulez). Puisque la précédence de [] est supérieure à & si vous avez la déclaration :

const int &a[1];

à cause de la précédence des opérateurs, vous vous retrouvez avec un tableau à un élément de références constantes à int. Si vous voulez que le & appliqué d'abord, vous devez forcer cela avec des parenthèses :

const int (&a)[1];  

Maintenant, vous avez une référence constante à un tableau d'ints à un élément. Dans la liste des paramètres de la fonction, il n'est pas nécessaire de préciser le nom d'un paramètre si vous ne l'utilisez pas, vous pouvez donc supprimer le nom, mais conserver les parenthèses :

size_t array_size(const int (&)[1])

1voto

MSN Points 30386

Rien ne se passe dans le tableau. C'est un paramètre inutilisé qui est utilisé pour résoudre la signature de la fonction modèle.

Il ne peut pas non plus être utilisé comme argument de modèle, mais c'est une autre question.

0voto

TechPriest Points 61

Une façon un peu bizarre d'obtenir le résultat en tant que const de compilation pour ceux d'entre nous qui n'ont pas de "constexpr" :

#include <iostream>

namespace
{

    template <size_t V>
    struct helper
    {
        enum
        {
            value = V
        };
    };

    template<typename T, size_t Size>
    auto get_size(T(&)[Size]) -> helper < Size >
    {
        return helper < Size >() ;
    }

    template<typename T>
    struct get_value
    {
        enum
        {
            value = T::value
        };
    };

}

int main()
{
    std::cout << get_value<decltype(get_size("Foo bar baz"))>::value;
}

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X