2017-01-13 1 views
14

Considérons le code suivant:modèle avec fonction surchargée homonymie `std :: enable_if` et contexte non déduit

template <typename T> 
struct dependent_type 
{ 
    using type = T; 
}; 

template <typename T> 
auto foo(T) -> std::enable_if_t<std::is_same<T, int>{}> 
{ 
    std::cout << "a\n"; 
} 

template<typename T> 
void foo(typename dependent_type<T>::type) 
{ 
    std::cout << "b\n"; 
} 
  • La première surcharge de foo peut déduire T de son invocation. La seconde surcharge de foo est une non-deduced context.

int main() 
{  
    foo<int>(1);  // prints "b" 
    foo<double>(1.0); // prints "b" 
    foo(1);   // prints "a" 
} 

Pourquoi foo<int>(1) print "b" et non "un"?

wandbox example

+0

Je suppose que la réponse est "b est plus spécialisé, donc toujours appelé si possible", et la raison serait quelque part [ici] (http://en.cppreference.com/w/cpp/language/function_template) (ça commence à mi-chemin de la page). C'est un peu beaucoup pour moi cependant. – Quentin

+0

Bonne question en effet. Je vous remercie.+1 – skypjack

+0

Pour la prochaine fois, je recommande de publier le code sous la forme d'un extrait unique, de sorte que les utilisateurs puissent copier/coller une seule fois, au lieu de devoir copier/coller deux fois pour essayer votre code. inclure des directives, ou démontrer la surcharge choisie différemment). –

Répondre

12

Essentiellement, les règles de commande partielles dire que la surcharge dependent_type est plus spécialisée en raison de ce contexte non déduit. Le processus de commande des fonctions de gabarit consiste à transformer les types de fonctions de gabarit et à effectuer une déduction de gabarit à tour de rôle, en commençant par le premier gabarit (le T) et le second (en prenant dependent_type), puis du second au premier.

Les règles sont beaucoup trop complexes pour être reproduites ici, mais allez lire [temp.func.order] et les passages auxquels elles sont liées si vous voulez les détails sanglants. Voici une simplification rapide:

Pour chaque paramètre de modèle de la fonction modèle, créez un type unique et remplacez le paramètre par celui-ci. Les types transformées pour cet exemple sont:

void foo(UniqueType); //ignoring the SFINAE for simplicity 
void foo(typename dependent_type<UniqueType>::type); 

Nous avons ensuite effectuer la déduction de modèle dans deux directions: une fois en utilisant les paramètres du premier modèle comme arguments à la seconde, et une fois en utilisant les paramètres du second comme arguments à la premier. Cela équivaut à effectuer la déduction sur ces appels de fonction:

//performed against template <class T> void foo(typename dependent_type<T>::type); 
foo(UniqueType{});      

//performed against template <class T> void foo(T);   
foo(dependent_type<UniqueType>::type{}); 

Dans le cadre de ces déductions, nous essayons de déterminer si une surcharge est plus spécialisée, puis l'autre. Lorsque nous essayons le premier, la déduction échoue, car typename dependent_type<T>::type est un contexte non déduit. Pour la seconde, la déduction réussit car dependent_type<UniqueType>::type est juste UniqueType, donc T est déduit à UniqueType.

Étant donné que la déduction a échoué à passer du second modèle au premier modèle, le deuxième modèle est considéré comme étant plus spécialisé que le premier. Le résultat final est que la résolution de surcharge préfère le second modèle pour foo<int>(1).

+0

Pourriez-vous nous en dire un peu plus sur ce que vous entendez par * "transformer" * et à quoi faites-vous référence lorsque vous utilisez les termes * "premier modèle" * et * "second template" *? Je comprends que les règles sont compliquées mais si je dois être honnête, votre réponse n'améliore pas du tout ma compréhension de cette situation. –

+3

@VittorioRomeo c'est très bien expliqué [ici] (http://stackoverflow.com/a/17008568/3953764). Le compilateur peut déduire 'T' quand' dependent_type :: type {} 'est utilisé comme argument, mais il ne peut pas déduire' T' de 'typename dependent_type :: type' quand' UniqueType {} 'est passé, donc la conclusion est que 'b' est plus spécialisé –

+1

@VittorioRomeo J'ai essayé de l'expliquer un peu plus, cela aide-t-il? – TartanLlama