Que signifie template <unsigned int N>?

121

Lors de la déclaration d'un template, j'ai l'habitude d'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 paramètre de type?

Igor Oks
la source

Réponses:

148

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

unsigned int x = N;

En fait, nous pouvons créer des algorithmes qui évaluent au moment de 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
}
maxaposteriori
la source
11
Vous pouvez également utiliser type static constexpr intau lieu de votre enum. Donc, le Factorial<0>modèle aurait static constexpr int value = 1, et template <int N> struct Factorialpeut avoirstatic constexpr int value = N * Factorial<N - 1>::value;
bobobobo
@bobobobo cela a été répondu avant C ++ 11 et constexpr.
Justin Meiners
154

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

  • Paramètres de type.
    • Les types
    • Modèles (uniquement des classes et des modèles d'alias, pas de fonctions ou de modèles de variables)
  • Paramètres non types
    • Pointeurs
    • Références
    • Expressions constantes intégrales

Ce que vous avez là-bas est du dernier genre. C'est une constante de temps de compilation (dite expression constante) et est de type entier ou énumération. Après l'avoir recherché dans la norme, j'ai dû déplacer les modèles de classe vers le haut dans la section des 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 néanmoins ces types. Vous pouvez avoir des pointeurs (et aussi des pointeurs membres) et des références à des objets / fonctions qui ont un lien externe (ceux qui peuvent être liés à partir d'autres fichiers objets et dont l'adresse est unique dans tout le 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 vers 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 (en passant 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 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
};
Johannes Schaub - litb
la source
Johannes, les modèles sont-ils classés sous «types»? Je pensais qu'ils étaient de quels types peuvent être fabriqués, mais pas les types eux-mêmes?
sbi
@sbi voir l'explication: "Après l'avoir recherché dans le standard, j'ai dû déplacer les modèles de classe vers le haut dans la section des 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. ". La note de bas de page 126 du 14.1 / 2 le dit. C'est juste une classification faite pour faire des paramètres non-type quelque chose qui déclare une valeur / référence et des paramètres de type quelque chose déclarant un nom de type ou un nom de modèle.
Johannes Schaub - litb
@ JohannesSchaub-litb donc il n'y a aucun moyen de taper template avec disons std :: string? comme la classe template <std :: string S> avec un compteur statique pour créer un identifiant unique pour chaque chaîne différente? la chaîne de hachage en int serait le seul moyen malheureusement non?
relaxxx
1
J'adorerais voir cette réponse complétée avec des objets membres de la classe de modèle, c'est-à-dire template <nom de type C, nom de type R, nom de type P1, nom de type P2> struct mystruct <R (C :: *) (P1, P2)>
Johnny Pauling
Le morceau de code avec SillyExamplene peut pas être compilé par GCC 4.8.4. La première erreur est the value of ‘flag’ is not usable in a constant expression. Il y a aussi d'autres erreurs
HEKTO
17

Vous modélisez votre classe sur la base d'un «int non signé».

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.
 }
Martin York
la source
15

Une classe de modèle est comme une macro, mais beaucoup moins maléfique.

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

La différence est que les paramètres ont des «types» et que les valeurs passées sont vérifiées lors de la compilation, comme les paramètres des fonctions. Les types valides sont vos types C ++ normaux, comme 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 se trouvait dans la définition d'origine. Tout comme une macro.

Vous pouvez également utiliser les types " class" ou " typename" pour les paramètres (ils sont vraiment les mêmes). Avec un paramètre de l'un de ces types, vous pouvez passer un nom de type au lieu d'une valeur. Tout comme avant, partout où le nom du paramètre était dans la définition de classe de modèle, dès que vous créez une nouvelle instance, devient le type que vous passez. Il s'agit de l'utilisation la plus courante d'une classe de modèle; Tout le monde qui sait quelque chose sur les modèles C ++ sait comment faire cela.

Considérez 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 le même que ce code utilisant une macro:

#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 sûr, la version du modèle est un milliard de fois plus sûre et plus flexible.

Jonathan
la source