Quelles sont les exigences pour les parameters de modèle C ++?

Si vous utilisez un modèle en C ++ qui prend une valeur entière en tant que paramètre, existe-t-il des exigences relatives à une variable entière utilisée en tant que paramètre qui sont différentes de celles utilisées si la variable était utilisée en tant que paramètre dans un appel de fonction?

Ceci est un suivi de la question ici . Je veux spécifiquement aborder s’il existe une différence entre les variables WRT déclarées “extern const int” pour des fonctions ou des modèles?

Je peux voir que dans certains cas de template, la valeur du paramètre serait nécessaire au moment de la compilation. Est-ce toujours vrai? Existe-t-il un moyen de spécifier, peut-être que pour certaines utilisations de la valeur de paramètre, que la valeur soit utilisée au moment de l’exécution?

Ce qui suit est issu de la norme.

14.3.2.1:

Un argument de modèle pour un paramètre de modèle non typé ou non template doit être l’un des suivants:

  • une expression constante intégrale de type intégral ou énumération; ou
  • le nom d’un modèle-paramètre non typé; ou
  • l’adresse d’un object ou d’une fonction avec une liaison externe, y compris les modèles de fonction et les identificateurs de modèle de fonction, à l’exclusion des membres de classe non statiques, exprimée sous la forme & expression_id, où & est facultatif si le nom fait référence à une fonction ou à un tableau, ou le modèle-paramètre correspondant est une référence; ou
  • un pointeur sur membre exprimé comme décrit en 5.3.1.

5.19.1:

À plusieurs endroits, C ++ requirejs des expressions qui se traduisent par une constante d’énumération ou d’intégrale: comme limites de tableau (8.3.4, 5.3.4), comme expressions de cas (6.4.2), comme longueurs de champ binary (9.6), comme initialiseurs d’énumérateur (7.2), en tant qu’initialiseurs de membre statique (9.4.2) et en tant qu’arguments de modèle de type non-énumération ou d’énumération (14.3).

constant-expression: conditional-expression 

Une expression constante intégrale ne peut impliquer que des littéraux (2.13), des énumérateurs, des variables const ou des membres de données statiques de types intégraux ou énumérés initialisés avec des expressions constantes (8.5), des parameters de modèle non types de types intégraux ou énumératifs et des expressions sizeof. Les littéraux flottants (2.13.3) peuvent uniquement apparaître s’ils sont convertis en types intégraux ou enumérations. Seules les conversions de types en types entiers ou en énumérations peuvent être utilisées. En particulier, sauf dans les expressions sizeof, les fonctions, les objects de classe, les pointeurs ou les références ne doivent pas être utilisés et les opérateurs d’affectation, d’incrémentation, de décrémentation, d’appel de fonction ou de virgule ne doivent pas être utilisés.

En ce qui concerne votre post précédent, je crois que l’essence de la partie “variables constantes … initialisées avec …” (et je ne pense pas que les initialisations externes comptent).

Ce doit être une expression constante intégrale. Cela est expliqué par le document standard à 5.19 :

Une expression constante intégrale ne peut impliquer que des littéraux (2.13), des énumérateurs, des variables const ou des membres de données statiques de types intégraux ou énumérés initialisés avec des expressions constantes (8.5), des parameters de modèle non types de types intégraux ou énumératifs et des expressions sizeof. Les littéraux flottants (2.13.3) peuvent uniquement apparaître s’ils sont convertis en types intégraux ou enumérations. Seules les conversions de types en types intégraux ou énumérés peuvent être utilisées.

Notez que “intégrale” est un autre terme pour “entier”, mais n’est pas identique à “int”. “char”, par exemple, a un type entier / entier, mais ce n’est évidemment pas le type int. Donc, concrètement, ce qui suit est permis

  • 10 or 10L or anything like that
  • enum { THIS, OR, THAT };
  • int const this_one = 10;
  • sizeof(char)
  • bien sûr, tout autre paramètre de modèle décrit ci-dessus

Chacun de ceux-ci peut être utilisé comme argument de modèle pour un paramètre qui possède un type intégral du type correspondant. Certaines conversions sont toujours appliquées cependant. Donc, s’il veut un int et que vous transmettez un caractère, il le promulguera automatiquement. Idem si vous fournissez un énumérateur et qu’il veut un int.

Donc, par ces règles, si vous avez

 extern const int SomeName; 

Et il ne voit pas de définition qui initialise cette constante avec une expression de constante intégrale, elle ne peut pas être utilisée comme argument de modèle. Mais il peut bien sûr être utilisé comme argument de fonction. Celles-ci n’ont pas besoin d’être connues au moment de la compilation car elles ne font pas partie d’un type. Au moment où vous nommez une spécialisation de modèle, les arguments que vous avez utilisés deviennent une partie du type:

 MyGreatStack // 4 is now part of the type MyGreatStack! 

Notez qu’il existe d’ autres moyens de passer SomeName en tant qu’argument. Cependant, tous ne peuvent pas être acceptés par un paramètre de modèle entier. Vous pouvez accepter ce qui précède par un paramètre de référence, par exemple

 template struct NowItWorks { }; 

Et il accepterait le SomeName ci-dessus. Désormais, plutôt qu’une valeur, un emplacement particulier unique dans l’ensemble du programme (car la variable a extern lien extern ) a été choisi.

Il est toujours vrai que la valeur de int est nécessaire au moment de la compilation.

Étant donné que chaque instanciation de modèle est un élément séparé du code compilé (même pour les parameters de modèle de nombre entier), cet entier doit être disponible lors de la compilation (et il doit être garanti que rien ne change).

C’est également pourquoi il est judicieux de ne pas utiliser les parameters de modèle entier lorsque vous allez utiliser un grand nombre de valeurs uniques – vous pouvez rapidement vous retrouver avec un énorme fichier exécutable.