Es gibt zwei ungelöste Fragen in Bezug auf friend
Funktionsschablonen in Klassen-Templates definiert: 1545 und 2174. Ersteres stellt die Frage, inwieweit es überhaupt gültig ist, und letzteres geht um odr-Verstöße, die aufgrund der tatsächlichen Instanziierungen dieser Funktionsvorlagen entstehen können. Ich bin mir nicht sicher, welcher Compiler richtig ist (bisher war ich der Meinung, dass beide falsch waren), aber im Standard ist es einfach zu schlecht oder schlecht spezifiziert, was das richtige Verhalten in dieser Situation ist.
Der Code sollte ideal kompilieren (Problemlösung pending):
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);
Die friend
Erklärung ersten get
erklärt, so entsteht ein neues Mitglied des innersten einschließenden Namespace: ::get
. Die externe Deklaration dekodiert einfach die gleiche Funktion, also gibt es wirklich nur die eine ::get
. Von [temp.over.link]:
Zwei Ausdrücke Template-Parametern die gleichwertig betrachtet werden, wenn zwei Definitionen Funktion die Ausdrücke enthalten, die eine Auflösungsregel (3.2), mit Ausnahme würde genügen, dass die Token verwendet, um die nennen Vorlagenparameter können abweichen, solange ein Token, das zum Benennen eines Vorlagenparameters in einem Ausdruck verwendet wird, durch ein anderes Token ersetzt wird, das denselben Vorlagenparameter im anderen Ausdruck benennt. Wenn ermittelt wird, ob zwei abhängige Namen (14.6.2) äquivalent sind, wird nur der Name selbst berücksichtigt, nicht das Ergebnis der Namenssuche im Kontext der Vorlage.
verschiedene Template-Parameter Namen (Args...
vs Args2...
) ist in Ordnung - diese zweite Erklärung der Funktion Vorlage ::get
ist gültig und ermöglicht Lookup es zu finden.
Dies bringt uns zu:
bool test(const obj<int, float, double> &a) {
#ifdef UNQUAL
return get<int>(a); // unqualified
#else
return ::get<int>(a); // qualified
#endif
}
diesen beiden sollte Arbeit - da wir die Funktion sein, in Namespacebereich neu deklariert, wir haben nicht einmal über ADL zu kümmern.Beide Aufrufe sollten ::get<int>(obj<int, float, double>)
finden, was ein friend
ist, und so sollte der Code kompilieren und verknüpfen. gcc erlaubt den unqualifizierten Anruf, aber nicht den qualifizierten Anruf, clang erlaubt es auch nicht.
Wir umgehen könnten beide CWG Probleme ganz einfach durch nicht innerhalb der Klasse der Funktionsvorlage definieren:
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; }
Mit dieser Formulierung erlauben beiden compiles sowohl qualifizierten und unqualifizierten Aufruf von get
.
Wenn wir den Code so umschreiben, dass obj
eine Klasse ist und nicht eine Klasse-Vorlage, aber alle anderen Faktoren gleich sind:
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
}
Beide Compiler erlauben beide Anrufungen. Aber es ist kein Unterschied, dass ich in den Regeln zwischen obj
eine normale Klasse und eine Klassenvorlage bewusst ist.
Warum erklärt 'template T get (konst obj &o);' außerhalb Klasse –
Jarod42
Weil es nicht, wenn Sie es nicht funktioniert ... versuchen: https://godbolt.org/g/peSscU –
Ja, 'T' ist nicht nachvollziehbar, also müssen Sie' 'angeben, damit ADL nicht funktioniert (da es keine Vorlage" get "im globalen Umfang gibt):/Sie können deklariere eine Dummy-Vorlage get (mit nicht korrekter Übereinstimmung), um ADL wieder zu aktivieren: [Demo] (http://coliru.stacked-crooked.com/a/e40aaf90de712943). –
Jarod42