2016-04-19 8 views
8

Im folgenden C++ Beispielcode GCC 6 und Clang 3.8 nicht einig, was das richtige Verhalten ist:Vorlagenobjekts der Vorlage Freund Funktionen und Namespaces

Das erfundene Beispiel „funktioniert“ - wie in der test() Funktion gibt o.p in GCC. In Klirren, ruft sie die (nicht definiert) Funktion get<int, int, float, double>:

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); 


bool test(const obj<int, float, double> &a) { 
return get<int>(a); 
} 

den gleichen Code in einem Namensraum Putting verursacht GCC das gleiche Klappern tut zu tun.

namespace ns { 

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); 

} 

bool test(const ns::obj<int, float, double> &a) { 
return ns::get<int>(a); 
} 

https://godbolt.org/g/sWrXQO und https://godbolt.org/g/9tIXwe

Welche Compiler „richtig“ ist und gibt es in der Regel eine Art und Weise zu definieren eine Template-Funktion Inline-Freund Mitglied ohne es zu erklären hat und es dann separat zu definieren. Das heißt, Dinge wie:

struct Foo { 
friend bool bar() { return true; } // declares *and* defines a free function bar 
template<typename T> T bar2() { return true; } // doesn't work! 
}; 
+0

Warum erklärt 'template T get (konst obj &o);' außerhalb Klasse – Jarod42

+0

Weil es nicht, wenn Sie es nicht funktioniert ... versuchen: https://godbolt.org/g/peSscU –

+0

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

Antwort

5

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.

+0

Danke für die umfassende Antwort. Ich schätze die Problemumgehung (um das Get external zu definieren) - und das mache ich bereits in meinem Code. Die Disparität zwischen der nicht-templatisierten Funktion kann jedoch inline definiert werden (und üblicherweise mit zB "friend operator" durchgeführt werden), und sobald die Funktion selbst templatisiert ist, muss ihre Deklaration und Definition getrennt werden ... ist einfach nur verwirrend. Aber ... es ist C++ :) –

+2

@MattG Die externe Vorlagenanforderung kann am besten in [diesem Kommentar] erklärt werden (http://stackoverflow.com/questions/2953684/why-doesnt-adl-find-function-templates#comment29830195_2953783). Außerdem bin ich nicht 100% sicher, dass meine Argumentation bezüglich der Compiler stimmt. Hoffentlich kommt jemand anders hier herein. – Barry

+1

CWG2174 und CWG1545 könnten relevant sein. –