Il y a deux questions non résolues concernant friend
modèles de fonction définis dans les modèles de classe: 1545 et 2174. La première questionne la mesure dans laquelle elle est valide et la dernière concerne les violations d'odr qui peuvent survenir en fonction des instanciations réelles de ces modèles de fonction. Je ne suis pas sûr quel compilateur a raison (ayant déjà cru que les deux étaient faux), mais il peut simplement être sous- ou mal spécifié dans la norme quel est le comportement correct dans cette situation.
Le code devrait compiler idéalement (en attendant la résolution des problèmes):
template<typename ...Args>
class obj {
bool p = false;
template<typename T, typename... Args2>
friend T get(const obj<Args2...> &o) { return o.p; }
};
template<typename T, typename... Args>
T get(const obj<Args...> &o);
La déclaration friend
premier déclare get
, donc cela crée un nouveau membre de l'espace de noms englobante la plus profonde: ::get
. La déclaration externe vient de redéclarer la même fonction, donc il n'y en a vraiment qu'une ::get
. De [temp.over.link]:
Deux expressions impliquant des paramètres du modèle sont considérés comme équivalents si deux définitions de fonctions contenant les expressions satisferait la règle d'une définition (3.2), à l'exception que les jetons utilisés pour nommer le Les paramètres de modèle peuvent différer tant qu'un jeton utilisé pour nommer un paramètre de modèle dans une expression est remplacé par un autre jeton qui nomme le même paramètre de modèle dans l'autre expression. Pour déterminer si deux noms dépendants (14.6.2) sont équivalents, seul le nom lui-même est considéré, pas le résultat de la recherche de nom dans le contexte du modèle.
En utilisant différents noms de paramètres du modèle (Args...
vs Args2...
) est très bien - cette deuxième déclaration du modèle de fonction ::get
est valide et permet pour la recherche pour le trouver.
Cela nous amène à:
bool test(const obj<int, float, double> &a) {
#ifdef UNQUAL
return get<int>(a); // unqualified
#else
return ::get<int>(a); // qualified
#endif
}
Ces deux devraient travail - puisque nous redéclarée la fonction d'être portée d'espace de noms, nous n'avons même pas à se soucier de l'ADL.Les deux appels devraient trouver ::get<int>(obj<int, float, double>)
, qui est un friend
, et donc le code devrait compiler et lier. gcc permet l'appel non qualifié mais pas l'appel qualifié, clang n'autorise ni l'un ni l'autre.
Nous pourrions contourner les problèmes CWG entièrement simplement pas définir le modèle de fonction à l'intérieur de la classe:
template<typename ...Args>
class obj {
bool p = false;
template<typename T, typename... Args2>
friend T get(const obj<Args2...> &o);
};
template<typename T, typename... Args>
T get(const obj<Args...> &o) { return o.p; }
Avec cette formulation, les deux compiles permettent à la fois appel qualifiés et non qualifiés de get
.
Si nous réécrivons le code tel que obj
est une classe et non un modèle de classe, mais toutes choses étant égales:
class obj {
bool p = false;
template <class T>
friend T get(const obj& o) { return o.p; }
};
template <class T> T get(const obj&);
bool test(const obj& a) {
#ifdef UNQUAL
return get<int>(a);
#else
return ::get<int>(a);
#endif
}
Les deux compilateurs permettent aux deux invocations. Mais il n'y a aucune différence que je connais dans les règles entre obj
étant une classe normale et un modèle de classe.
Pourquoi déclarer 'template T get (const obj &o); classe' en dehors –
Jarod42
Parce que cela ne fonctionne pas si vous ne ... essayez: https://godbolt.org/g/peSscU –
Oui, 'T' n'est pas déductible, donc vous devez fournir' ', donc ADL ne fonctionne pas (car il n'y a pas de modèle' get' dans la portée globale):/Vous pouvez déclarer un modèle fictif obtenir (avec une correspondance incorrecte) pour réactiver l'ADL: [Demo] (http://coliru.stacked-crooked.com/a/e40aaf90de712943) –
Jarod42