2017-02-06 4 views
3

Considérez ce morceau de code:appel Ambigu lorsque vous appelez la fonction récursive modèle variadique surcharge

template<typename FirstArg> 
void foo() 
{ 
} 

template<typename FirstArg, typename... RestOfArgs> 
void foo() 
{ 
    foo<RestOfArgs...>(); 
} 

int main() 
{ 
    foo<int, int, int>(); 
    return 0; 
} 

Il ne compile pas en raison de l'appel foo<RestOfArgs...>(); ambigu quand RestOfArgs n'a qu'un seul élément ({int}).

Mais cette compile sans erreur:

template<typename FirstArg> 
void foo(FirstArg x) 
{ 
} 

template<typename FirstArg, typename... RestOfArgs> 
void foo(FirstArg x, RestOfArgs... y) 
{ 
    foo(y...); 
} 

int main() 
{ 
    foo<int, int, int>(5, 6, 7); 
    return 0; 
} 

Pourquoi est-il une ambiguïté dans le premier cas?

Pourquoi n'y a-t-il pas d'ambiguïté dans le second cas?

+0

Mon hypothèse est qu'il a quelque chose à voir avec le fait que la signature de la fonction sont ses arguments, et que «» et '' sont indiscernables dans un cas, mais dans l'autre quelquechose. – yeputons

+0

Regardez [overload_resolution] (http://en.cppreference.com/w/cpp/language/overload_resolution) et [Function_template_overloading] (http://en.cppreference.com/w/cpp/language/function_template#Function_template_overloading) – Jarod42

+0

@ Jarod42 Je l'ai regardé mais je ne connais toujours pas la réponse à mes questions. –

Répondre

1

Pourquoi y a-t-il ambiguïté dans le premier cas?

RestOfArgs peut être vide.

Alors foo<int> peut être instancié comme:

template<int> 
void foo() 
{ 
} 

et

template<int,> 
void foo() 
{ 
    foo<>(); 
} 

les deux seront compilés, il est ambigu.

En fait foo<>() ne compilera pas, mais il échoue dans l'instanciation suivante, donc cela n'a pas d'importance.

Pourquoi n'y a-t-il pas d'ambiguïté dans le second cas?

foo<int>(7) peut être instancié comme:

template<int> 
void foo(int 7) 
{ 
} 

et

template<int> 
void foo(int 7) 
{ 
    foo(); 
} 

mais le second est une erreur, car il n'y a pas foo prendre aucun argument, donc le seul candidat est le premier un, donc il n'y aura pas d'ambiguïté

+0

Cela vous dérangerait-il d'expliquer [cet exemple] (http://ideone.com/ZQz8Le)? J'ai supprimé les appels 'foo' et il compile toujours. – yeputons

+0

@yeputons sûr, sans appel récursif, seul le modèle VAARG est utilisé. –

+0

@yeputons, n'utilisez pas SO comme un service de consultation. C'est une bonne réponse à votre question. Si vous avez d'autres questions, veuillez poster une autre question. –

0

The answer by @ZangMingJie répond à la différence de comportement que vous observez dans votre code.

je l'ai trouvé plus facile de comprendre la résolution de nom avec la modification suivante:

template<typename FirstArg> 
void foo() 
{ 
    printf("1\n"); 
} 

template<typename FirstArg, typename SecondArg, typename... RestOfArgs> 
void foo() 
{ 
    printf("2\n"); 
    foo<SecondArg, RestOfArgs...>(); 
} 

int main() 
{ 
    foo<int, int, int>(); 
    return 0; 
} 

Quand il y a des paramètres sont utilisés deux modèles ou plus, la seconde fonction s'invoquée. Quand il y a un paramètre de template, la première fonction est invoquée.

+0

Cela fonctionne et résout le problème de l'ambiguïté, mais je ne sais toujours pas pourquoi. Ma question originale n'a pas encore reçu de réponse. –

+0

@rubix_addict, je pensais que la réponse de Zang a bien expliqué le problème. –

+0

@RSahu: Je ne pense pas, comme il l'explique, SFINAE sur le contenu de la définition de la fonction serait utilisé, ce qui est faux: -/ – Jarod42

1

Dans Function template overloading

Il y a beaucoup de règles à dire qui fonctionne modèle est plus spécialisé (selon les paramètres donnés).

Le point qui fait

template<typename> void foo(); 
template<typename, typename...> void foo(); 

ambiguë pour foo<int>(), mais pas

template<typename T> void foo(T); 
template<typename T, typename... Ts> void foo(T, Ts...); 

pour foo(42) est le suivant:

In case of a tie, if one function template has a trailing parameter pack and the other does not, the one with the omitted parameter is considered to be more specialized than the one with the empty parameter pack.

+0

Cette phrase ne suggère-t-elle pas qu'il ne devrait y avoir aucune ambiguïté dans les deux cas? Eh bien, le "paquet de paramètres de fin" est un peu ... ambigu (jeu de mots) - il peut s'appliquer à la fois au "paquet de paramètres de modèle final" et au "paquet de paramètres de fonction de fin". –

+0

Je suis d'accord que ce n'est pas assez clair IMO, mais "trailing" ne s'appliquera pas à 'template void f (Premier, std :: tuple , Trailing)' vs 'template void f (A, std :: tuple <>, B)'. ce qui est similaire à vos cas. – Jarod42

+0

mais dans mes deux cas les paquets de paramètres sont "traînants" –