2009-02-05 3 views
3

Dois-je explicitement instancier le type d'un gabarit de fonction lorsqu'il s'agit de la déduction du type de référence. Si tel est le cas, où est l'ambiguïté? Comparons 2 suivant des extraits de code:Déduction des types de référence dans les fonctions de gabarit

1er: link for the code

template <typename T> 
void foo(T& var, void(*func)(T&)) // T must be instantiated with int and it does . 
{ 
++var; 
} 
void ret(int & var){} 
int main() 
{int k =7; 
foo(k, &ret); 
cout<<k;//prints 8 
} 

Maintenant retirons s decleration 's dans foo()' & et nous avons une erreur.

2ème: link for the code

template <typename T> 
void foo(T var, void(*func)(T)) // T must be instantiated with int& but it doesn't. 
{ 
++var; 
} 

void ret(int & var){} 

int main() 
{int k =7; 

foo(k, &ret); //error: no matching function for call to 'foo(int&, void (*)(int&))' 
cout<<k; 
} 

Toutefois, si j'appelle foo par instancier explicitement <int&> "foo<int&>(k,&ret);", le code donne le même résultat que l'ancien. Quelle est la raison de cette erreur? Où est l'ambiguïté?

Merci.

Répondre

3

Comme le souligne Fionn de answer, le problème est que le compilateur en déduit deux types distincts pour T, int et int &. 18.8.2.4/2 a les suivantes:

Dans certains cas, la déduction est effectuée à l'aide d'un seul ensemble de types P et A, dans d'autres cas, il y aura un ensemble de types correspondants P et A. La déduction de type est effectuée indépendamment pour chaque paire P/A et les valeurs d'argument de modèle déduites sont ensuite combinées.Si la déduction de type ne peut être effectuée pour une paire P/A ou si pour une paire quelconque la déduction conduit à plus d'un ensemble possible de valeurs déduites, ou si des paires différentes donnent des valeurs différentes ou si un argument de modèle reste ni déduit, ni explicitement spécifié, modèle argument deduction failed.

Le texte surligné je crois couvre votre exemple. Vous n'avez pas demandé, mais une option que vous avez potentiellement est d'utiliser deux paramètres de modèle dans votre exemple. Ces deux cas peuvent être déduits, de sorte que vous pouvez ensuite utiliser une autre tricherie de gabarit éventuellement via activer si créer un nouveau type qui représente la version que vous voulez, c'est-à-dire. avec référence ou sans.

+0

Comment se fait-il que "int" et "int &" puissent être déduits? Il n'y a pas de fonction déclarant le paramètre "int", mais seulement "int &". void ret (int & var) {}. donc T ne peut être que "int &", sinon la signature de ret (int & var) ne convient pas à T. i.e ret (int var) n'est pas possible. Merci pour votre réponse. –

+0

Donc, il n'y a pas de jeux "P" et "A" dans mon exemple. Il n'y a que P {qui est int &}. "A" ne peut pas être possible comme je l'ai dit dans mon précédent commentaire. Je pense qu'il y a une autre raison à cette ambiguïté mais je ne la trouve pas :( –

+0

Dans l'appel foo (k, ret): 'int' est déduit de 'k-> T', et 'int &' est déduit de ' ret 'avec type' void (int &) '->' void (*) (T) 'Votre ensemble P/A est: A {k, ret} et P {T, void (*) (T)}. selon le texte standard, 'T' est déduit pour chaque paire P/A et il n'en résulte pas le même type. –

1

Deuxième version de la réponse, tout a mal interprété la question:

Le problème avec la deuxième version est que le compilateur ne peut pas savoir si vous voulez passer par référence ou passer par valeur.

La syntaxe d'appel pour les deux est exactement la même, mais vous voulez évidemment passer par référence - sinon l'incrément n'a aucun sens.

Voici un exemple pour montrer qu'il n'y a pas de différence comment vous appelez une fonction avec un laissez-passer par référence et une avec un laissez-passer par l'argument de valeur:

void Incr1(int &value) { value++; } 
void Incr2(int value) { value++ } //Completely useless but for demonstration 

//Now the calling of both functions 
int x = 1; 
Incr1(x); 
Incr2(x); 

Si vous utilisez un pointeur au lieu d'une référence, la Le compilateur sait quoi faire parce que vous lui dites explicitement que vous passez un pointeur sur int.

#include <iostream> 

template <typename T> 
void foo(T var, void(*func)(T)) 
{ 
    ++(*var); 
} 

void ret(int *var){} 

int main() 
{ 
    int k =7; 

    foo(&k, &ret); 
    std::cout<<k; 
} 

Cela fera le compiler, mais il n'y a pas beaucoup de sens dans ce:

L'erreur est que votre fonction prend toujours un T & et non un T.

Vous juste besoin cette petite modification:

template <typename T> 
void foo(T var, void(*func)(T&)) 
{ 
++var; 
} 

void ret(int & var){} 

int main() 
{ 
    int k =7; 

    foo(k, &ret); 
    std::cout<<k; 
} 
+0

@Fionn - pourriez-vous clarifier votre réponse? Je sais que vous avez raison: la fonction ret() et le pointeur de fonction dans le modèle doivent avoir exactement les mêmes signatures. Mais ce n'est pas si évident dans le texte de votre réponse. – Arkadiy

+0

@Fionn, Non, vous avez tort. Cela instancie T avec "int" pas "int &" et imprime 7 pas 8. et je n'ai pas demandé "juste instancier ce code sans erreur" plutôt "l'instancier sans instanciation explicite avec référence afin que j'aie la même sortie" . Merci, après tout. –

+0

Maintenant, je comprends ce que vous essayez de faire. Le problème est sans référence explicite que le compilateur ne peut pas savoir si vous voulez passer par valeur ou référence. Il ne peut passer par référence que s'il est requis de passer par référence. – Fionn