Je regardais la mini-bibliothèque FastDelegate de Don Clugston et j’ai remarqué une astuce syntaxique étrange avec la structure suivante:
TemplateClass Object;
Il semble presque qu’une signature de fonction soit utilisée comme argument pour une déclaration d’instance de modèle.
Cette technique (dont la présence dans FastDelegate est apparemment due à un Jody Hagins) a été utilisée pour simplifier la déclaration des instances de modèle avec un nombre semi-arbitraire de parameters de modèle.
À savoir, cela permettait quelque chose comme ceci:
// A template with one parameter template struct Object1 { _T1 m_member1; }; // A template with two parameters template struct Object2 { _T1 m_member1; _T2 m_member2; }; // A forward declaration template struct Object; // Some derived types using "function signature"-style template parameters template struct Object : public Object1 {}; template struct Object : public Object2 {}; // A. "Vanilla" object declarations Object1 IntObjectA; Object2 IntCharObjectA; // B. Nifty, but equivalent, object declarations typedef void UnusedType; Object IntObjectB; Object IntCharObjectB; // C. Even niftier, and still equivalent, object declarations #define DeclareObject( ... ) Object DeclareObject( int ) IntObjectC; DeclareObject( int, char ) IntCharObjectC;
Malgré la véritable odeur de piratage, je trouve que ce type d’émulation de spoofy des arguments de modèles variadic est époustouflant.
Le vrai détail de cette astuce semble être le fait que je peux transmettre des constructions textuelles comme “Type1 (Type2, Type3)” comme arguments aux modèles. Voici donc mes questions: comment le compilateur interprète-t-il exactement cette construction? Est-ce une signature de fonction? Ou est-ce juste un modèle de texte avec des parenthèses? Si le premier, alors cela implique-t-il que toute signature de fonction arbitraire est un type valide en ce qui concerne le processeur de modèle?
Une question de suivi serait que, puisque l’exemple de code ci-dessus est un code valide, pourquoi le standard C ++ ne permet-il pas de faire quelque chose comme ce qui suit, qui ne comstack pas?
template struct Object { _T1 m_member1; }; // Note the class identifier is also "Object" template struct Object { _T1 m_member1; _T2 m_member2; }; Object IntObject; Object IntCharObject;
En ce qui concerne votre première question – concernant le type int(char, float)
– il s’agit d’un type C ++ valide et est le type d’une fonction qui prend un char
et un float
et retourne un int
. Notez que c’est le type de la fonction réelle, pas un pointeur de fonction, qui serait un int (*) (char, float)
. Le type réel de toute fonction est ce type inhabituel. Par exemple, le type de
void DoSomething() { /* ... */ }
est void ()
.
La raison pour laquelle cela ne se produit pas beaucoup pendant la programmation de routine est que dans la plupart des cas, vous ne pouvez pas déclarer de variables de ce type. Par exemple, ce code est illégal:
void MyFunction() { void function() = DoSomething; // Error! }
Cependant, un des cas où vous voyez réellement les types de fonctions utilisés est le passage de pointeurs de fonctions autour de:
void MyFunction(void FunctionArgument()) { /* ... */ }
Il est plus courant de voir ce genre de fonction écrite pour prendre en compte un pointeur de fonction, mais il est tout à fait normal de prendre en compte la fonction elle-même. Il se fait couler dans les coulisses.
En ce qui concerne votre deuxième question, pourquoi il est illégal d’avoir le même modèle écrit avec un nombre différent d’arguments, je ne connais pas exactement le libellé de la spécification qui l’interdit, mais cela a quelque chose à voir avec le fait déclaré un modèle de classe, vous ne pouvez pas modifier le nombre d’arguments. Cependant, vous pouvez fournir une spécialisation partielle sur ce modèle qui contient un nombre différent d’arguments, à condition bien sûr que la spécialisation partielle ne soit spécialisée que sur le nombre initial d’arguments. Par exemple:
template class Function; template class Function { /* ... */ };
Ici, Function
prend toujours un paramètre. La spécialisation du modèle prend deux arguments, mais la spécialisation ne concerne toujours qu’un seul type (spécifiquement, Ret (Arg)
).
int* int_pointer; // int_pointer has type "int*" int& int_reference; // int_reference has type "int&" int int_value; // int_value has type "int" void (*function_pointer)(int, int); // function_pointer has type // "void (*)(int, int)" void (&function_reference)(int, int); // function_reference has type // "void (&)(int ,int)" void function(int, int); // function has type // "void(int, int)" template<> struct Object1 { void m_member1(int, int); // wait, what?? not a value you can initialize. };