2011-03-05 3 views
11

J'ai récemment tenté de créer une classe is_class et j'avais besoin d'un moyen pour le compilateur de différencier les types d'énumération et les types de classe pour lesquels les opérateurs de conversion sont définis. Voyant comment les classes, les structures et les unions sont les seuls types compatibles avec les fonctions pointeur vers membre, j'ai décidé de demander au compilateur de déterminer si le type utilisé pour instancier le template is_class était, à son tour, compatible avec les fonctions pointers-to-member . Après avoir rencontré plusieurs problèmes, j'ai décidé de tester le comportement des énumérations lorsqu'elles étaient utilisées conjointement avec des pointeurs vers des membres et obtenaient des résultats farfelus. Le segment suivant illustre la première bizarrerie:Énumérations et pointeur vers membres

enum ENUM {}; 
void Test(void (ENUM::*pmem) (void)) 
{ 
    /* ... */ 
} 
Test(NULL); 

Lors de la compilation avec Microsoft Visual C++ 2010, la partie pointeur à membre de la définition de la fonction: (ENUM::*pmem)

est en rouge et la souris sur la déclaration révèle l'erreur:

Error: "ENUM" is not a class type

Cependant, le compilateur parse ce segment sans rencontrer d'erreurs, l'attribution pmem-NULL. Il est intéressant pour moi que le compilateur permette de voir comment les types d'énumération ne sont pas des classes, des structures ou des unions et ne peuvent donc pas posséder leurs propres méthodes.

Le deuxième point d'intérêt a surgi lors de la création d'une fonction de modèle, en prenant un pointeur à membre l'argument dont le type varie:

template<class _Ty> 
void Test_Template(void (_Ty::*pmem) (void)) 
{ 
    /* ... */ 
} 

Bien sûr, afin de pouvoir utiliser cette fonction, il doit être explicitement qualifié:

Test_Template<ENUM>(NULL); 

Cet appel cependant, génère une erreur indiquant:

invalid explicit template argument(s) for 'void Test(void (__thiscall _Ty::*)(void))'

J'ai corrigé ce problème en créant un modèle de fonction supplémentaire, dont le prototype correspondrait à tout appel qui ne correspondrait pas au prototype de la fonction modèle précédente (qui impliquait l'utilisation d'une ellipse).

Questions:

  1. Pourquoi est une énumération compatible avec les pointeurs-à-membres?

  2. Pourquoi existe-t-il une correspondance exacte lors de l'appel de la fonction Test non modèle alors que le compilateur génère une erreur pour la qualification explicite Test_Template du modèle?

+0

Il semble que 'T ENUM :: * D;' soit une déclaration syntaxiquement valide. Je ne peux rien trouver qui dit explicitement qu'il est sémantiquement mal formé. Mais s'il est bien formé, ce serait une déclaration pour laquelle la section 8 ne spécifie pas le type de «D», ce qui serait bizarre. – aschepler

+3

merci pour la question, c'est génial de voir des questions au-delà du niveau de base de temps en temps et celui-ci devient mon esprit barattage :) –

+0

g ++ 4.4.5 -std = C++ 0x accepte de même 'Test', mais sur un tentative d'appeler 'Test_Template (static_cast (0))' donne l'erreur "aucune fonction correspondante pour l'appel à' Test_Template (void (ENUM :: *)()) '" – aschepler

Répondre

2

En ce qui concerne votre première question, il semble que le compilateur rapporte en effet que les énumérations ne peuvent pas avoir des fonctions membres, puisque le compilateur signale une erreur sur la déclaration de fonction. Il est probable que l'appel réussisse en essayant en interne de corriger autant que possible la mauvaise déclaration, ce qui signifie dans ce cas que vous essayez de déclarer quelque chose comme un pointeur et d'autoriser l'appel. Il n'y a aucune exigence que le compilateur vous donne une erreur sur cette ligne; Puisque le programme est formé, tant que le compilateur rejette le programme avec un diagnostic, il n'a pas besoin de donner des erreurs partout.En ce qui concerne votre deuxième question, la raison pour laquelle un second modèle fait disparaître l'erreur est le "substitution failure is not an error" (SFINAE) principle. Lorsque le compilateur instancie un modèle de fonction avec certains arguments de type, s'il trouve qu'une instanciation de fonction particulière est invalide (par exemple, en essayant d'obtenir un pointeur sur un membre d'une énumération), elle ne signale pas d'erreur. Au lieu de cela, il supprime simplement ce modèle de la considération. Si, cependant, aucun des modèles que vous avez écrits n'est valide lorsqu'il est instancié avec les arguments donnés, alors le compilateur émettra une erreur am parce qu'il ne peut pas trouver une correspondance pour ce que vous essayez de faire. Dans le premier cas, lorsque vous avez un seul gabarit, l'erreur se produit car SFINAE élimine le seul gabarit du modèle, ce qui fait que le gabarit du gabarit n'a pas de gabarit correspondant. Dans le second cas, votre modèle "attrape-tout" est toujours valide après l'instanciation du modèle, donc si le modèle qui prend un pointeur vers un membre est exclu, il existe toujours un modèle légal auquel vous pouvez vous référer. Par conséquent, le code est parfaitement bien.

Questions connexes