2016-07-25 13 views
3

Von Lippman et al C++ Primer 5. Auflage, Abschnitt 16.1.2:Freund Vergleich und relationalen Operatoren in C++ Klassenvorlage

//forward declarations needed for friend declarations in Blob 
template <typename> class BlobPtr; 
template <typename> class Blob; 
template <typename T> bool operator==(const Blob<T>&, const Blob<T>&) 

template <typename T> class Blob { 
    friend class BlobPtr<T>; 
    friend bool operator==<T>(const Blob<T>&, const Blob<T>&); 
} 

Erste Frage: in der Linie

friend bool operator==<T>(const Blob<T>&, const Blob<T>&); 

Warum ist die <T> nach == vorhanden? Warum nicht einfach schreiben

Ich habe den folgenden Code hinzugefügt, um Operator == zu definieren und die Klassenvorlage zu instanziieren. Es kompiliert erfolgreich und Links:

template <typename T> 
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {return true;} 

int main() { 
    Blob<int> a, b; 
    a == b; 
} 

Wenn ich entfernen die <T> folgende operator== in der Freund Erklärung, ich einen Linker Fehler:

Undefined symbols for architecture x86_64: "operator==(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-3ccda9.o 

deutlich die <T> folgende operator== notwendig ist, aber warum?

Zweite Frage: Wenn ich die relationale weniger als Operator < für die gleiche Klasse definieren wollen, ich würde vermuten, dass ich das Muster folgen sollte, die für == gearbeitet:

1) voraus erklären die Betreiber

2) erklären die Betreiber als Freund, Einfügen der zusätzlichen <T>, deren Funktion die ich nicht verstehe

3) out-of-Klasse, um die Betreiber zu definieren.

Ich füge daher den folgenden Code:

template <typename T> bool operator<(const Blob<T>&, const Blob<T>&); 
template <typename T> class Blob { 
    //other members as before 
    friend bool operator<<T>(const Blob<T>&, const Blob<T>&); 
} 
bool operator<(const Blob<T>&, const Blob<T>&) {return true;} 
int main() { 
    //other statements as before 
    a < b; 
} 

Dies um operator<<T> einen Kompilierungsfehler erzeugt, ich denke, da der Compiler << als Ausgabe-Operator interpretiert. Aber wenn ich den Freund Erklärung umschreiben als

friend bool operator<(const Blob<T>&, const Blob<T>&); 

dann bekomme ich einen Linker Fehler ähnlich den früheren Linkerfehler mit ==:

"operator<(Blob<int> const&, Blob<int> const&)", referenced from: _main in partial_blob-a85d5d.o 

Wie kann ich erfolgreich Operator definiert < für diese Klasse?

(Anmerkung:. Die Betreiber als Freunde deklariert werden muss, weil mehr vollständig realisiert Implementierungen auf private Variablen verlassen)

+1

Denken Sie daran, dass Friend-Funktionen, die so deklariert sind, keine Member-Funktionen sind, sie sind Nicht-Member-Funktionen und ohne die '' ist die Funktion nicht abgeschlossen. Wie für 'Operator <' versuchen, ein Leerzeichen zwischen dem Operatornamen und der Vorlage wie 'Operator < ' hinzuzufügen. –

+0

Über die undefinierte Referenz und ihre Ursache und Lösung (wie Sie bereits wissen); http://StackOverflow.com/a/35891188/3747990 – Niall

+0

@JoachimPileborg - Können Sie einen Verweis auf eine Einführung in diese Syntax geben? Ich weiß nicht einmal, wie es heißt, also habe ich Probleme, nachzuschlagen. Ist es eine Template-Spezialisierung? Explizite Instanziierung? Websuchen nach diesen Begriffen liefern keine hilfreichen Ergebnisse. – Chad

Antwort

0

Ich posten meine eigene Antwort, Anerkennung Joachim Pileborg und songyuanyao für die Richtung.

Ich habe den Code vereinfacht, um nur auf Frage 1 zu konzentrieren. Pileborg und Holt wiesen richtig darauf hin, dass das Überladen < lediglich einen Platz benötigt, um dem Compiler beim Parsen zu helfen.

template <typename> class Blob; 
template <typename T> bool operator==(const Blob<T>&, const Blob<T>&); //line 2 

template <typename T> class Blob { 
    friend bool operator==(const Blob<T>&, const Blob<T>&); //line 5 
}; 

template <typename T> 
bool operator==(const Blob<T> &lhs, const Blob<T> &rhs) {return true;} //line 9 

int main() { 
    Blob<int> a, b; //line 12 
    a == b; //line 13 
} 

Dieser Code erzeugt einen Fehler bei der Verbindungszeit. Um zu verstehen warum, schauen wir uns die relevante Sprache aus dem Standard an.

Aus dem C++ 14 Standard n4296, 14.5.4 (Siehe unten für eine Zusammenfassung der hier verwendeten Terminologie).

Nun schauen wir uns die Freundschaftsdeklaration in Zeile 5 an und bestimmen, worauf sie sich gemäß den vier oben aufgeführten Schritten bezieht.

(1.1) == ist keine Template-ID; weitergehen ...

(1.2) == ist keine qualifizierte ID; weitergehen ...

(1.3) == ist keine qualifizierte ID; weitergehen ...

(1.4) daher ist == eine unqualifizierte ID, die eine Nicht-Template-Funktion deklariert (oder deklariert).

Gemäß Abschnitt 7.3.3 des Standards wird der Freund == im innersten umschließenden Namespace deklariert - in diesem Fall dem globalen Namespace.

Wenn wir in Zeile 12 Blob<int> instanziieren, generiert der Compiler Quellcode, indem int für alle Vorkommen von T in Klasse Blob ersetzt. Der Freund Erklärung im Compiler generierten Code lautet dann:

friend bool operator==(const Blob<int>&, const Blob<int>&); 

So haben wir erklärt, eine (nicht Vorlage) Überlastung der operator== im globalen Namensraum, mit Parametern des Typs const Blob<int>&.

Wenn in Zeile 12 a == b aufgerufen wird, beginnt der Compiler den Prozess der Überladungsauflösung. Es sucht zuerst nach Nicht-Template-Überladungen, die den Argumenttypen entsprechen. Es findet eine perfekte Übereinstimmung in der Form der operator== deklariert, wenn Blob<int> instanziiert wurde. Der Linker sucht dann nach der Definition von operator== entsprechend der Best-Match-Deklaration, findet aber keine, weil die operator==(const Blob<int>&, const Blob<int>&) nie wirklich definiert wurde!

Die Lösung ist eine Vorlage-ID zu verwenden, wie der Name in der Freund-Deklaration (die keine Template-Deklaration ist):

friend bool operator== <>(const Blob<T>&, const Blob<T>&) 

oder

friend bool operator== <T>(const Blob<T>&, const Blob<T>&) 

Beide operator== <> und operator== <T> sind template -id's (siehe Terminologie unten); Ersteres hat eine implizite Template-Parameterliste, die von der Funktionsparameterliste abgeleitet ist, und letzteres hat eine explizite Template-Parameterliste.

Wenn Blob<int> auf der Leitung 12 instanziiert wird, erzeugt der Compiler den folgenden Code für die Freund-Deklaration:

friend bool operator== <>(const Blob<int>&, const Blob<int>&) 

oder

friend bool operator== <int>(const Blob<int>&, const Blob<int>&) 

In jedem Fall wird der Name des Freundes ist ein uneingeschränkter Template-ID, also nach (1.1) oben bezieht sich die Friend-Deklaration auf eine Spezialisierung einer Funktionsvorlage. Der Compiler findet dann eine optimale Vorlage für die angeforderte <int> Spezialisierung. Die einzige gefundene Vorlage ist die in Zeile 2 deklarierte und in Zeile 9 definierte Vorlage, die sie wie gewünscht aufruft.

Terminology

qualifizierte-ID: eine Kennung mit einem Scoping Bediener angebracht, z.B. std::string oder ::i

unqualifizierte-id: ein Bezeichner ohne einen angeschlossenen Scoping-Operator, z.B. string oder i.

Template-ID: Der folgende Auszug (Aus dem C++ 14 Standard-n4296, 14.2) einen Überblick über die Struktur der Vorlage-ids:

So einige Template-IDs würde Foo<T> und ==<T> umfassen. (== ist eine Operator-Funktions-ID).Beachten Sie, dass template <> im Gegensatz zu einer Vorlage-Deklaration nicht in einem Template-ID-Ausdruck enthalten ist.

2

why is the <T> present after == ? Clearly the <T> following operator== is necessary, but why?

Da operator== in Freund Erklärung bezieht sich auf die Funktionsvorlage, müssen Sie es explizit angeben . Andernfalls wird eine Nicht-Template-Funktion deklariert, aber ihre Definition kann später nicht gefunden werden. Es ist nicht dasselbe wie das Aufrufen (und Instanziieren) der Funktionsschablone.

Hinweis T könnte weggelassen werden, aber <> wird noch benötigt.Wie zum Beispiel:

// refers to a full specialization of operator== 
friend bool operator== <>(const Blob<T>&, const Blob<T>&); 

Ein weiterer Kandidat Weg zu ist der Bediener innerhalb der Klassendeklaration definieren, die inline sein und könnte als Nicht-Template-Funktion deklariert werden. Wie zum Beispiel:

template <typename T> class Blob { 
    ... 
    friend bool operator==(const Blob&, const Blob&) { 
     return ...; 
    } 
} 

This produces a compilation error around operator<<T>

Ja, wie Sie gesagt haben, sollte es als friend bool operator< <T>(...) oder friend bool operator< <>(...), oder sehen Sie meinen Vorschlag über Nicht-Template-Funktion Freund geschrieben werden.

0

First question: in the line

friend bool operator==<T>(const Blob<T>&, const Blob<T>&); 

why is the <T> present after == ? Why not simply write

friend bool operator==(const Blob<T>&, const Blob<T>&); 

Wenn Sie die <T> entfernen, gibt gcc eine Warnung:

warning: friend declaration ' bool operator==(const Blob< <template-parameter-1-1> >&, const Blob< <template-parameter-1-1> >&) ' declares a non-template function [-Wnon-template-friend]

Sie ein Freund von Ihrer Klasse eine nicht-Templat-Funktion zu machen, so dass der Compiler/Linker für eine nicht suchen -Templated Funktion, in Ihrem Fall:

bool operator==(const Blob<int>&, const Blob<int>&); 

... die nicht existiert, so kann der Linker es nicht finden.

Wenn Sie nicht <T> hinzufügen (oder <> zum friend Erklärung), müssen Sie für jeden Typ eine Funktion definieren, die wahrscheinlich nicht das, was Sie wollen.

Second question: If I want to define the relational less than operator < for the same class, I would guess that I should follow the pattern that worked for ==:

Dies ist ein einfaches Problem mit der Art und Weise C++ Code analysiert wird, benötigen Sie einen Raum zwischen operator< und << einzufügen. Dies ist das gleiche Problem, das vor C++ 11 bestand, wo Sie vector<vector<int> > anstelle von vector<vector<int>> wegen >> verwenden mussten.