web-dev-qa-db-fra.com

Que signifie modèle <unsigned int N>?

Lorsque je déclare un modèle, je suis habitué à avoir ce genre de code:

template <class T>

Mais dans cette question , ils ont utilisé:

template <unsigned int N>

J'ai vérifié qu'il compile. Mais qu'est-ce que ça veut dire? Est-ce un paramètre non-type? Et si oui, comment pouvons-nous avoir un modèle sans aucun paramètre de type?

106
Igor Oks

Il est parfaitement possible de modéliser une classe sur un entier plutôt que sur un type. Nous pouvons affecter la valeur basée sur un modèle à une variable ou la manipuler d'une manière que nous pourrions utiliser avec tout autre littéral entier:

unsigned int x = N;

En fait, nous pouvons créer des algorithmes qui évaluent à la compilation (à partir de Wikipedia ):

template <int N>
struct Factorial 
{
     enum { value = N * Factorial<N - 1>::value };
};

template <>
struct Factorial<0> 
{
    enum { value = 1 };
};

// Factorial<4>::value == 24
// Factorial<0>::value == 1
void foo()
{
    int x = Factorial<4>::value; // == 24
    int y = Factorial<0>::value; // == 1
}
135
maxaposteriori

Oui, c'est un paramètre non typé. Vous pouvez avoir plusieurs types de paramètres de modèle

  • Paramètres de type.
    • Les types
    • Modèles (uniquement les modèles de classe et d'alias, pas de fonction ni de modèle de variable)
  • Paramètres non typés
    • Pointeurs
    • Les références
    • Expressions constantes intégrales

Ce que vous avez là est du dernier type. C'est une constante de temps de compilation (appelée expression constante) et elle est de type entier ou énumération. Après avoir cherché dans la norme, j'ai dû déplacer les modèles de classe dans la section types, même si les modèles ne sont pas des types. Mais ils sont appelés paramètres de type dans le but de décrire ces types. Vous pouvez avoir des pointeurs (et également des membres) et des références à des objets/fonctions ayant un lien externe (liens pouvant être liés à partir d'autres fichiers objet et dont l'adresse est unique dans l'ensemble du programme). Exemples:

Paramètre de type de modèle:

template<typename T>
struct Container {
    T t;
};

// pass type "long" as argument.
Container<long> test;

Paramètre entier du modèle:

template<unsigned int S>
struct Vector {
    unsigned char bytes[S];
};

// pass 3 as argument.
Vector<3> test;

Paramètre de pointeur de modèle (passage d'un pointeur à une fonction)

template<void (*F)()>
struct FunctionWrapper {
    static void call_it() { F(); }
};

// pass address of function do_it as argument.
void do_it() { }
FunctionWrapper<&do_it> test;

Paramètre de référence du modèle (passage d'un entier)

template<int &A>
struct SillyExample {
    static void do_it() { A = 10; }
};

// pass flag as argument
int flag;
SillyExample<flag> test;

Paramètre de modèle de modèle.

template<template<typename T> class AllocatePolicy>
struct Pool {
    void allocate(size_t n) {
        int *p = AllocatePolicy<int>::allocate(n);
    }
};

// pass the template "allocator" as argument. 
template<typename T>
struct allocator { static T * allocate(size_t n) { return 0; } };
Pool<allocator> test;

Un modèle sans aucun paramètre n'est pas possible. Mais un modèle sans aucun argument explicite est possible - il a des arguments par défaut:

template<unsigned int SIZE = 3>
struct Vector {
    unsigned char buffer[SIZE];
};

Vector<> test;

Syntaxiquement, template<> est réservé pour marquer une spécialisation de modèle explicite, au lieu d’un modèle sans paramètres:

template<>
struct Vector<3> {
    // alternative definition for SIZE == 3
};
141

Vous modélisez votre classe sur la base d'un 'unsigned int'.

Exemple:

template <unsigned int N>
class MyArray
{
    public:
    private:
        double    data[N]; // Use N as the size of the array
};

int main()
{
    MyArray<2>     a1;
    MyArray<2>     a2;

    MyArray<4>     b1;

    a1 = a2;  // OK The arrays are the same size.
    a1 = b1;  // FAIL because the size of the array is part of the
              //      template and thus the type, a1 and b1 are different types.
              //      Thus this is a COMPILE time failure.
 }
15
Martin York

Une classe de modèles est comme une macro, mais beaucoup moins perverse.

Pensez à un modèle comme une macro. Les paramètres du modèle sont substitués dans une définition de classe (ou fonction) lorsque vous définissez une classe (ou fonction) à l'aide d'un modèle.

La différence est que les paramètres ont des "types" et que les valeurs transmises sont vérifiées lors de la compilation, comme les paramètres des fonctions. Les types valides sont vos types C++ habituels, tels que int et char. Lorsque vous instanciez une classe de modèle, vous transmettez une valeur du type que vous avez spécifié et, dans une nouvelle copie de la définition de classe de modèle, cette valeur est remplacée là où le nom du paramètre était dans la définition d'origine. Juste comme une macro.

Vous pouvez également utiliser les types "class" ou "typename" (ils sont vraiment les mêmes). Avec un paramètre de l'un de ces types, vous pouvez transmettre un nom de type au lieu d'une valeur. Comme auparavant, partout où le nom du paramètre figurait dans la définition de la classe de modèle, dès que vous créez une instance, il devient le type que vous transmettez. C'est l'utilisation la plus courante pour une classe de modèle. Tous ceux qui connaissent les modèles C++ savent comment faire cela.

Considérons cet exemple de code de classe de modèle:

#include <cstdio>
template <int I>
class foo
{
  void print()
  {
    printf("%i", I);
  }
};

int main()
{
  foo<26> f;
  f.print();
  return 0;
}

C'est fonctionnellement identique à ce code utilisant des macros:

#include <cstdio>
#define MAKE_A_FOO(I) class foo_##I \
{ \
  void print() \
  { \
    printf("%i", I); \
  } \
};

MAKE_A_FOO(26)

int main()
{
  foo_26 f;
  f.print();
  return 0;
}

Bien entendu, la version du modèle est un milliard de fois plus sûre et plus flexible.

13
Jonathan