54 votes

Expressions de type signature comme arguments de modèle C ++

Je cherchais à Ne Clugston de FastDelegate mini-bibliothèque et a remarqué un étrange syntaxique truc avec la structure suivante:

TemplateClass< void( int, int ) > Object;

Il semble presque comme si une signature de fonction est utilisé comme un argument pour un modèle de déclaration.

Cette technique (dont la présence dans FastDelegate est apparemment due à un Jody Hagins) a été utilisé pour simplifier la déclaration des instances de modèle avec un semi-nombre arbitraire de paramètres du modèle.

À savoir, il a permis à ce quelque chose comme ce qui suit:

// A template with one parameter
template<typename _T1>
struct Object1
{
    _T1 m_member1;
};

// A template with two parameters
template<typename _T1, typename _T2>
struct Object2
{
    _T1 m_member1;
    _T2 m_member2;
};

// A forward declaration
template<typename _Signature>
struct Object;

// Some derived types using "function signature"-style template parameters
template<typename _Dummy, typename _T1>
struct Object<_Dummy(_T1)> : public Object1<_T1> {};

template<typename _Dummy, typename _T1, typename _T2>
struct Object<_Dummy(_T1, _T2)> : public Object2<_T1, _T2> {};

// A. "Vanilla" object declarations
Object1<int> IntObjectA;
Object2<int, char> IntCharObjectA;

// B. Nifty, but equivalent, object declarations
typedef void UnusedType;
Object< UnusedType(int) > IntObjectB;
Object< UnusedType(int, char) > IntCharObjectB;

// C. Even niftier, and still equivalent, object declarations
#define DeclareObject( ... ) Object< UnusedType( __VA_ARGS__ ) >
DeclareObject( int ) IntObjectC;
DeclareObject( int, char ) IntCharObjectC;

En dépit de la vraie bouffée de hackiness, je trouve ce genre de spoofy émulation de variadic template arguments assez hallucinant.

La vraie viande de cette astuce semble être le fait que je peux passer textuelle des constructions comme "Type1(Type2, Type3)" comme des arguments de modèles. Voici donc mes questions: Comment fonctionne exactement le compilateur interpréter cette construction? Est-ce une signature de fonction? Ou, est-il un modèle de texte avec des parenthèses-t-il? Dans le premier cas, alors est ce que cela implique que toute fonction arbitraire de signature est un type valide jusqu'au modèle de processeur est concerné?

Une question de suivi serait que, depuis l'exemple de code ci-dessus est valable de code, pourquoi ne pas le C++ standard vous permet tout simplement de faire quelque chose comme ce qui suit, qui ne compile pas?

template<typename _T1>
struct Object
{
    _T1 m_member1;
};

// Note the class identifier is also "Object"
template<typename _T1, typename _T2>
struct Object
{
    _T1 m_member1;
    _T2 m_member2;
};

Object<int> IntObject;
Object<int, char> IntCharObject;

44voto

templatetypedef Points 129554

En ce qui concerne votre première question sur le type int(char, float) - ce qui est valable en C++ et est le type de fonction qui prend en char et float et renvoie un int. Notez que c'est le type de la fonction, pas un pointeur de fonction, ce qui serait une int (*) (char, float). Le type réel de n'importe quelle fonction est de ce type inhabituel. Par exemple, le type de

void DoSomething() {
    /* ... */
}

est - void ().

La raison pour que cela ne vient pas jusqu'à bien au cours de la routine de programmation, c'est que dans la plupart des cas, vous ne pouvez pas déclarer des variables de ce type. Par exemple, ce code est illégal:

void MyFunction() { 
    void function() = DoSomething; // Error!
}

Cependant, un cas où vous ne vous en fait voir des types de fonction est utilisé pour le passage de pointeurs de fonction autour de:

void MyFunction(void FunctionArgument()) {
     /* ... */
}

Il est plus courant de voir ce genre de fonction écrite dans un pointeur de fonction, mais il est parfaitement bien de prendre dans la fonction elle-même. Il obtient coulé derrière-le-scènes.

Quant à votre deuxième question, pourquoi c'est illégal d'avoir le même modèle écrit avec différents nombres d'arguments, je ne sais pas exactement le libellé de la spécification qui l'interdit, mais il a quelque chose à voir avec le fait qu'une fois que vous avez déclaré un modèle de classe, vous ne pouvez pas changer le nombre d'arguments pour elle. Cependant, vous pouvez fournir une spécialisation partielle sur ce modèle qui a un nombre différent d'arguments, à condition bien sûr que la spécialisation partielle seulement se spécialise sur l'origine nombre d'arguments. Par exemple:

template <typename T> class Function;
template <typename Arg, typename Ret> class Function<Ret (Arg)> { 
    /* ... */
};

Ici, Function toujours prend un paramètre. Le modèle de la spécialisation prend deux arguments, mais la spécialisation n'est encore que plus d'un type (plus précisément, Ret (Arg)).

4voto

ephemient Points 87003
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(int, int)>
{
    void m_member1(int, int);  // wait, what?? not a value you can initialize.
};

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X