2010-08-28 35 views
4

J'ai une classe de modèle avec une fonction de membre de modèle. Je veux instancier explicitement la classe pour éviter un ralentissement drastique de la compilation. J'utilise g ++ 4.1.2. Je reçois des erreurs de spécialisation de modèle ambigu du compilateur. Ce code le plus court qui reproduira le problème:Instanciation de modèle explicite avec la fonction de modèle de membre

template <class T, class S > 
class Test 
{ 
public: 
template< typename T1 > 
void* get(const T1&); 
void* get(const int&); //Specialization of the above 
}; 

typedef Test<int, double> foo; 

//instantiate 
inline template class Test<int, double>; 
template void* foo::get(int const&); 

Je ne veux pas utiliser un fourre-tout:

template class Test<int, double> 

parce que le get de surcharge (const int &) ne sera pas défini pour Toutes les instanciations explicites possibles et donc le compilateur jettera un ajustement pour les types qui ne le supportent pas.

Ce code est compilé dans Visual Studio (sans le modèle précédent en ligne, qui est une extension spécifique de gcc). Quelqu'un peut-il jeter un peu de lumière sur la façon dont je compile ce fragment de code?

MISE À JOUR: Ce est l'erreur que je reçois:

g++ -c -o template.o template.cpp 
template.cpp:14: error: ambiguous template specialization ‘get<>’ for ‘void* Test<int, double>::get(const int&)’ 
template.cpp:7: error: candidates are: void* Test<T, S>::get(const int&) [with T = int, S = double] 
template.cpp:6: error:     template<class T1> void* Test::get(const T1&) [with T1 = T1, T = int, S = double] 

MAJ2: Merci pour la solution, il ne compile pas bien. Les spécialisations ne sont pas autorisées dans une classe. L'erreur est:

g++ -c -o template.o template.cpp 
template.cpp:7: error: explicit specialization in non-namespace scope ‘class Test<T, S>’ 
template.cpp:7: error: enclosing class templates are not explicitly specialized 
template.cpp:8: error: ‘get’ is not a template function 
template.cpp: In instantiation of ‘void* Test<T, S>::get(const T1&) [with T1 = int, T = int, S = double]’: 
template.cpp:15: instantiated from here 
template.cpp:15: error: explicit instantiation of ‘void* Test<T, S>::get(const T1&) [with T1 = int, T = int, S = double]’ but no definition available 
make: *** [template.o] Error 1 
+0

Est-ce que 'foo' est censé être' Test'? Pouvez-vous poster l'erreur réelle et la ligne à laquelle elle fait référence? –

+0

J'ai typedef'd truc pour une saisie facile. L'erreur réelle est: g ++ -c -o template.o template.cpp template.cpp: erreur: spécialisation de modèle ambigu 'get <>' pour 'void * Test :: get (const int &)' template .cpp: 7: erreur: les candidats sont: void * Test :: get (const int &) [avec T = int, S = double] template.cpp: 6: erreur: modèle void * Test :: get (const T1 &) [avec T1 = T1, T = int, S = double] – Venkatesan

+0

Oups, je n'ai pas vu ce 'typedef', désolé. –

Répondre

2

Je suis confus par ceci:

I don't want to use a catch-all:

template class Test<int, double> 

because the overload get(const int&) will not be defined for all possible explicit instantiations and hence the compiler will throw a fit for types which dont support it.

Explicitness d'une spécialisation ne touche pas la sémantique d'autres spécialisations. La surcharge get(const int&) est simplement une fonction membre, et elle sera instanciée pour les spécialisations explicites et implicites comme n'importe quelle autre.

L'instanciation explicite ne peut que ralentir le compilateur. Il ne traite qu'une seule fois chaque instanciation. Les parties inutilisées d'une instanciation implicite peuvent être ignorées, mais en instanciant explicitement, vous le forcez à gérer le tout. Pas qu'une seule instanciation est susceptible de prendre un temps mesurable, de toute façon.

Pour exécuter par des erreurs dans le code:

template <class T, class S > // arguments unused 
class Test 
{ 
public: 
template< typename T1 > 
void* get(const T1&); 
void* get(const int&); // overload of the above 
}; 

typedef Test<int, double> foo; 

// illegal to instantiate a template before its members are defined 

inline template class Test<int, double>; // "inline template" is meaningless 
template void* foo::get(int const&); // typedef name "foo" cannot be used here 
/*^illegal to explicitly instantiate a member of an already explicitly 
    instantiated template */ 

Mise à jour:

Les résultats d'erreur de la surcharge non-modèle ne pas avoir la priorité sur le modèle de membre.

Malheureusement, vous cannot explicitly specialize un modèle de membre d'un modèle parent. La solution de contournement à cette question est de se spécialiser partiellement, mais cela ne fonctionnera pas parce que vous avez un modèle de fonction.

Solution de contournement n ° 2 est SFINAE.

#include <boost/enable_if.hpp> 
#include <boost/type_traits.hpp> 

template< typename T1 > 
boost::disable_if< boost::is_same<T1,int>, void* >::type 
    get(const T1&); // "turn off" declaration if in conflict 

void* get(const int&); // unambiguous overload of the above 

Si vous ne pouvez pas utiliser Boost,

template< class T > 
struct disable_if_int { typedef void *type; }; 

template<> 
struct disable_if_int<int> {}; 

...

template< typename T1 > 
disable_if_int<T1>::type get(const T1&); 

void* get(const int&); // unambiguous overload of the above 
+0

Dans gcc, si vous instanciez avec inline, aucune fonction membre n'est intensionnée et vous pouvez le faire en fonction des besoins. Si vous n'utilisez pas "inline" lors de l'instanciation, tous les membres du template sont instanciés. Ref: http://gcc.gnu.org/onlinedocs/gcc/Template-Instantiation.html Désolé pour le mauvais phrasé, je voulais la possibilité d'instancier tous les membres nécessaires en utilisant en ligne et obtenu l'erreur en essayant de le faire . – Venkatesan

+0

@Venkatesan: "tous les membres nécessaires"? Selon la page que vous avez liée, aucun membre n'est instancié par cela. – Potatoswatter

0
template <class T, class S > 
class Test 
{ 
public: 
template< typename T1 > 
void* get(const T1&) { return nullptr; } 

template <> 
void* get<int>(const int&) { return nullptr; } //Specialization of the above 
}; 

typedef Test<int, double> foo; 

int main() { 
    foo lols; 
    void* innit = lols.get(1); 
    void* bruv = lols.get("yocakes"); 
} 

Cette compile très bien pour moi sur VS2010. nullptr est C++ 0x btw, mais vous pouvez juste remplacer par 0/NULL.

+0

14.7.3/2: "Une spécialisation explicite doit être déclarée ... pour les modèles de membre, dans l'espace de noms dont la classe englobante ou le modèle de la classe englobante est membre." En outre, vous avez entièrement supprimé les instanciations explicites, ce qui permet de contourner la question. – Potatoswatter