Paramètres de modèle non-type


93

Je comprends que le paramètre de modèle non-type doit être une expression intégrale constante. Quelqu'un peut-il nous expliquer pourquoi?

template <std::string temp>
void foo()
{
     // ...
}
error C2993: 'std::string' : illegal type for non-type template parameter 'temp'.

Je comprends ce qu'est une expression intégrale constante. Quelles sont les raisons de ne pas autoriser les types non constants comme std::stringdans l'extrait de code ci-dessus?


17
Un paramètre de modèle est résolu au moment de la compilation.
Etienne de Martel

Réponses:


121

La raison pour laquelle vous ne pouvez pas faire cela est que les expressions non constantes ne peuvent pas être analysées et substituées pendant la compilation. Ils pourraient changer pendant l'exécution, ce qui nécessiterait la génération d'un nouveau modèle pendant l'exécution, ce qui n'est pas possible car les modèles sont un concept de compilation.

Voici ce que la norme autorise pour les paramètres de modèle non de type (14.1 [temp.param] p4):

Un paramètre de modèle non de type doit avoir l'un des types suivants (éventuellement qualifiés cv):

  • type intégral ou énumération,
  • pointeur sur objet ou pointeur sur fonction,
  • lvalue référence à l'objet ou lvalue référence à la fonction,
  • pointeur vers le membre,
  • std::nullptr_t.

6
@ALOToverflow: Cela relève du "pointeur vers le membre". C'est soit "pointeur vers la fonction membre", soit "pointeur vers les données membres".
Xeo

4
Il convient de noter que pour le cas des pointeurs vers des objets (ou des champs d'instance), les objets doivent avoir une durée de stockage statique et une liaison (externe pré C ++ 11, interne ou externe en C ++ 11), de sorte que les pointeurs vers eux puissent être créé au moment de la compilation.
Theodore Murdock

2
En C ++ 20, cela est désormais autorisé à condition que le type ait une forte égalité structurée, soit un littéral, aucun sous-objet mutable / volatil et que l'opérateur du vaisseau spatial soit public.
Rakete1111

73

Cela n'est pas autorisé.

Cependant, ceci est autorisé:

template <std::string * temp> //pointer to object
void f();

template <std::string & temp> //reference to object
void g();

Voir §14.1 / 6,7,8 dans C ++ Standard (2003).


Illustration:

template <std::string * temp> //pointer to object
void f()
{
   cout << *temp << endl;
}

template <std::string & temp> //reference to object
void g()
{
     cout << temp << endl;
     temp += "...appended some string";
}

std::string s; //must not be local as it must have external linkage!

int main() {
        s = "can assign values locally";
        f<&s>();
        g<s>();
        cout << s << endl;
        return 0;
}

Production:

can assign values locally
can assign values locally
can assign values locally...appended some string

7
@Mahesh: Parce que le modèle sur lequel se base essentiellement les modèles est l'adresse de ce std::stringpointeur ou objet de référence. Si cette variable était locale, vous obtiendriez probablement des adresses différentes à chaque fois que la fonction est appelée.
Xeo

11
@Mahesh: Vous ne savez pas à quoi ressemble la pile d'appels au moment de la compilation. Avant votre fonction, il aurait pu être appelé 10 autres ou 3 autres ou plusieurs autres, donc l'adresse sur la pile, à laquelle la chaîne est créée, peut changer d'un appel à l'autre. Lorsque vous avez un objet avec une liaison externe, son adresse est fixée lors de la compilation / liaison.
Xeo

2
@Xeo " son adresse est fixe lors de la compilation / liaison. " Ou pas, pour du code relocalisable ou indépendant de la position.
curiousguy

1
Cette réponse (actuellement) ne semble pas répondre à la question du PO, qui demandait pourquoi ce comportement existe; cette réponse ne fait que reprendre l'exemple du PO sans offrir aucune explication.
Quuxplusone

1
Je suis en retard pour la fête, il semble que les pointeurs intelligents ne fonctionnent pas non plus
Nicholas Humphrey

28

Vous devez pouvoir modifier les arguments du modèle

template <std::string temp>
void f() {
 // ...
}

f<"foo">();
f<"bar">(); // different function!?

Maintenant, un impl devrait proposer une séquence unique de caractères pour un std::string ou, d'ailleurs, toute autre classe arbitraire définie par l'utilisateur, stockant une valeur particulière, dont la signification n'est pas connue de l'implémentation. De plus, la valeur des objets de classe arbitraires ne peut pas être calculée au moment de la compilation.

Il est prévu d'envisager d'autoriser les types de classe littéraux en tant que types de paramètres de modèle pour post-C ++ 0x, qui sont initialisés par des expressions constantes. Ceux-ci pourraient être mutilés en ayant les membres de données mutilés de manière récursive en fonction de leurs valeurs (pour les classes de base, par exemple, nous pouvons appliquer la profondeur d'abord, de gauche à droite). Mais cela ne fonctionnera certainement pas pour les classes arbitraires.


10

Un argument de modèle non-type fourni dans une liste d'arguments de modèle est une expression dont la valeur peut être déterminée au moment de la compilation. Ces arguments doivent être:

expressions constantes, adresses de fonctions ou d'objets avec lien externe, ou adresses de membres statiques de la classe.

En outre, les littéraux de chaîne sont des objets avec un lien interne, vous ne pouvez donc pas les utiliser comme arguments de modèle. Vous ne pouvez pas non plus utiliser de pointeur global. Les littéraux à virgule flottante ne sont pas autorisés, étant donné la possibilité évidente d'erreurs d'arrondi.


Vous avez utilisé une citation, alors quelle est la source? Je voudrais lui donner un avis positif s'il a une source pour la citation.
Gabriel Staples le
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.