Question

J'essaie de créer une fonction:

template <typename T>
void doIt( T*& p )
{
   if ( !p ) { return; }
   T& ref = *p;
   getClassName( ref );
}

où le comportement varie en fonction du type de p transmis. En particulier, la version de getClassName appelée doit dépendre du type de getClassName( std::vector<T,A>& ). Dans l'exemple suivant, je peux appeler avec succès:

doIt<myClass1>( myClass1*& )
doIt<myClass1<int> >( myClass1*& )
doIt<myClass2>( myClass2*& )
doIt<myClass2<int> >( myClass2*& )

mais cela échoue lorsque j'appelle:

doIt< std::vector<int, std::allocator<int> > >( std::vector<int, std::allocator<int>>*& )

avec l'erreur:

a.cxx: In function ‘void doIt(T*&) [with T = std::vector<int, std::allocator<int> >]’:
ba.cxx:87:   instantiated from here
a.cxx:33: error: invalid initialization of reference of type ‘MyClass1&’ from expression of type ‘std::vector<int, std::allocator<int> >’
a.cxx:16: error: in passing argument 1 of ‘const char* getClassName(MyClass1&)’

(gcc 4.2.4).

Si je propose la déclaration de:

template<typename T, typename A>
char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }

avant doIt - alors il compile. Alors,

  • Pourquoi faut-il que doIt apparaisse avant getClassName( MyClass2T<T>& ) mais pas std::vector
  • Que puis-je faire pour rendre <=> indépendant de <=>? (Je veux pouvoir placer <=> dans son propre en-tête sans avoir à connaître <=>, ni aucune des spécialisations, qui seront définies par l'utilisateur).

.

#include <stdio.h>
#include <assert.h>
#include <vector>

//template<typename T>
//char const* getClassName( T& );

//template<typename T, typename A>
////char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }

#if 1
// ---------  MyClass2
struct MyClass1
{};

char const* getClassName( MyClass1& ) { printf("MyClass1\n"); return NULL; }

// ---------  MyClass1T
template< typename T>
struct MyClass1T
{};

template<typename T>
char const* getClassName( MyClass1T<T>& ) { printf("MyClass1T<T>\n"); return NULL; }
#endif


template <typename T>
void doIt( T*& p )
{
   if ( !p ) { return; }
   T& ref = *p;
   getClassName( ref );
}


// ---------  MyClass2
struct MyClass2
{};


// declared after doIt, OK.
char const* getClassName( MyClass2& ) { printf("MyClass2\n"); return NULL; }

// ---------  MyClass2T
template< typename T>
struct MyClass2T
{};

// declared after doIt, OK.
template<typename T>
char const* getClassName( MyClass2T<T>& ) { printf("MyClass2T<T>\n"); return NULL; }

template<typename T, typename A>
char const* getClassName( std::vector<T,A>& ) { printf("std::vector<T,A>\n"); return NULL; }



void test()
{
#if 1
   MyClass1 mc1;
   MyClass1* mc1p = &mc1;
   doIt( mc1p );

   MyClass2 mc2;
   MyClass2* mc2p = &mc2;
   doIt( mc2p );

   MyClass1T<int> mc1t;
   MyClass1T<int>* mc1tp = &mc1t;
   doIt( mc1tp );

   MyClass2T<int> mc2t;
   MyClass2T<int>* mc2tp = &mc2t;
   doIt( mc2tp );

   // Nested templates are OK.
   MyClass2T<MyClass1> mc2t2;
   MyClass2T<MyClass1>* mc2tp2 = &mc2t2;
   doIt( mc2tp2 );
#endif

#if 1
   std::vector<int> v;
   std::vector<int>* vp = &v;
   doIt( vp );                   // FAIL!
#endif
}
Était-ce utile?

La solution

  

Pourquoi faut-il que getClassName (std :: vector & amp;) apparaisse avant doIt mais pas getClassName (MyClass2T & amp;)

Une déclaration dans la portée est requise pour toute fonction. Lorsque vous instanciez votre fonction de modèle avec un vector<int> élément, une fonction portant la signature getClassName(vector<int>&) doit être présente (au moins un prototype) pour que la compilation aboutisse.

  

Que puis-je faire pour que doIt soit indépendant de std :: vector? (Je veux pouvoir placer doIt dans son propre en-tête et ne pas avoir à connaître std :: vector, ni aucune des spécialisations, qui seront définies par l'utilisateur)

Lisez la FAQ sur les modèles . Essayez de placer le prototype de toutes les fonctions de modèle dépendant de doIt avant la première instanciation de <=>.

Autres conseils

La raison de l’échec est qu’au moment de l’instanciation, aucune recherche de nom non qualifiée n’a lieu pour les fonctions (mais uniquement ADL - Recherche dépendante de l’argument). Le contexte d'instanciation est (tiré de 14.6.4.1/6 de la norme C ++):

  

Le contexte d'instanciation d'une expression qui dépend des arguments de modèle est l'ensemble des déclarations avec liaison externe déclarées avant le point d'instanciation de la spécialisation de modèle dans la même unité de traduction.

Le point d'instanciation de toutes les spécialisations de modèles que vous avez appelées dans ce cas se situe juste après la définition de test (lire 14.6.4.1/1). Ainsi, toutes les fonctions que vous avez déclarées sont visibles dans votre getClassName fonction à l'aide d'une recherche non qualifiée, mais leur recherche est en réalité différente pour les appels de fonction:

Un appel de fonction qui dépend d'un paramètre de modèle dans un modèle est recherché comme suit:

  • Les noms du contexte de définition de modèle sont considérés à la fois par la recherche ordinaire et par ADL.
  • Les noms du contexte d'instanciation sont considérés uniquement pour ADL.

Cela signifie que, puisqu'il n'y a pas de std::vector<T> fonction appropriée déclarée dans le contexte de définition du modèle, une fonction appropriée doit être trouvée dans le contexte d'instanciation à l'aide d'ADL - sinon l'appel échouera et ne trouvera aucune déclaration.

Recherche dépendante de l'argument (ADL)

Pour un argument de type std, ADL recherche des fonctions dans l'espace de noms T et dans l'espace de noms de ADL. Le fait de placer la fonction doIt dans l'espace de noms MyClass2 fonctionnerait pour cela (mais cela n'est pas autorisé par la norme, car cela engendre un comportement indéfini - cela ne devrait être fait qu'en dernier recours).

Pour voir les effets de int essayez d'appeler T = MyClass2 avec un vecteur de std::vector<MyClass2> au lieu de MyClass1. Depuis lors 14.6.4.2/1, ADL recherchera dans l’espace de nommage de <=> une fonction appropriée acceptant un <=> et réussira - contrairement à lorsque vous utiliserez <=>, qui ne cherchera que dans <=>.

Pour les autres appels de fonction, leurs déclarations respectives sont toutes trouvées, car elles sont toutes déclarées dans l'espace de noms global, dans lequel les types d'argument des appels de fonction sont également définis (<=>, <=>, etc.).

La FAQ C ++ est bonne, mais elle n’est pas approfondie dans les modèles (n’y trouve aucune mention d’ADL). Il existe une modèle faq dédiée qui traite certains des pièges les plus complexes. .

Méfiez-vous des comportements non définis

Notez que de nombreux compilateurs accepteront le code même si vous mettez cette déclaration, j’ai montré après la fonction <=> (au lieu d’avant). Mais comme le dit la citation standard ci-dessus, la déclaration ne fera pas partie du contexte d'instanciation et la règle trouvée dans <=> doit être surveillée:

  

Si l'appel est mal formé ou s'il trouve une meilleure correspondance, la recherche dans les espaces de noms associés prend en compte toutes les déclarations de fonction avec liaison externe introduites dans ces espaces de noms dans toutes les unités de traduction, sans tenir compte uniquement des déclarations trouvées dans le modèle. Dans les contextes de définition et d’instanciation de modèle, le programme a un comportement indéfini.

Ainsi, ce qui semblerait fonctionner serait un comportement indéfini. Il est valide pour un compilateur de l'accepter, mais il est également valable pour un de le rejeter ou de se bloquer et de se terminer. Veillez donc à ce que tout nom nécessaire soit effectivement visible dans le contexte d'instanciation, comme expliqué.

J'espère que cela vous aidera.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top