Wie deklarieren wir eine Friend-Funktion mit einer Klassenvorlage in einer .h-Datei und definieren sie in einer .cpp-Datei (nicht alle in einer Header-Datei)?

Lesezeit: 2 Minuten

Benutzer-Avatar
König der Löwen

Beim Trennen der Deklaration/Definition von (einer Friend-Funktion + einer Klassenvorlage) tritt ein Fehler auf:
error LNK2001: unresolved external symbol "class std::basic_ostream<char,struct std::char_traits<char> > & __cdecl operator<<(class std::basic_ostream<char,struct std::char_traits<char> > &,class Property<int> const &)" ([email protected][email protected][email protected]@[email protected]@@[email protected]@[email protected][email protected]@@@Z)

Beachten Sie, dass bei Verwendung einer Friend-Funktion ohne Klassenvorlage oder umgekehrt alles gut funktioniert, aber wenn die beiden Dinge zusammen verwendet werden, tritt ein Fehler auf.

Ich habe versucht, die Vorlage in der .cpp-Datei zu instanziieren, aber es war nicht erfolgreich.
Außerdem habe ich die .cpp-Datei am Ende der .h-Datei eingefügt, aber es hat auch nicht funktioniert.

Klasse.h

template <class PropertyType>
class Property {
    PropertyType m_property;
public:
    const PropertyType& operator=(const PropertyType& value);
    friend std::ostream& operator<<(std::ostream& os, Property<PropertyType>& other);
}; 

Klasse.cpp

template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
    m_property = value;
    return m_property;
}

template <class PropertyType>
std::ostream& operator<<(std::ostream& os, Property<PropertyType>& other) {
    os << other.m_property;
    return os;
}

template Property<int>;

main.cpp

int main() {
    Property<int> num;
    num = 100;
    std::cout << num << "\n";
}

Was ist falsch daran, wenn die Deklaration/Definition in zwei Dateien aufgeteilt und zusammen verwendet werden?

  • friend std::ostream& operator<<(std::ostream& os, Property<PropertyType>& other); ist keine Vorlage, also die Funktion, die Sie deklariert haben class.cpp ist damit nicht verwandt.

    – NathanOliver

    19. April um 16:06 Uhr

  • Auch wenn die Deklaration und Definition tat match, Warum können Templates nur in der Header-Datei implementiert werden?

    – molbnilo

    19. April um 16:30 Uhr

  • Wenn ich sie alle in einer .h-Datei zusammenfüge, funktioniert alles gut und das weiß ich, aber meine Frage hängt von der Trennung der Deklaration von der Definition ab, ist das möglich und wie?

    – König der Löwen

    19. April um 16:59 Uhr

  • Ich denke, das Q wurde fälschlicherweise als dieses Q gekennzeichnet. Der Autor hat die Technik bereits im Q verwendet, indem er erklärt hat template class Property<int>; in der .cpp-Datei. Aber das Problem ist etwas anderes. Das Problem ist, dass der Autor den Operator<< nicht instanziiert hat. Wenn Sie es verwenden, wird es funktionieren.

    – Gupta

    19. April um 17:07 Uhr


  • Hinzufügen template std::ostream& operator<<(std::ostream& os, Property<int>& other);am Ende Ihrer .cpp-Datei. Deklarieren Sie außerdem Ihre friend operator << als Vorlage. Dann wird es funktionieren.

    – Gupta

    19. April um 17:10 Uhr

Benutzer-Avatar
Anoop Rana

Es gibt zwei Probleme mit Ihrem Code-Snippet.

Das erstes Problem ist, dass Sie die Implementierung in die Quelldatei anstelle der Header-Datei eingefügt haben. Um dies zu lösen, verschieben Sie einfach die Implementierung in die Header-Datei.

Das zweites Problem ist, dass selbst wenn Sie die Implementierung in die Quelldatei verschieben, das Programm immer noch nicht funktioniert (Demo). Dies liegt daran, dass die Freundschaftserklärung die Sie derzeit haben (für überladen opearator<<), ist für eine gewöhnliche (Nicht-Vorlagen-)Funktion. Das heißt, in Ihrem ursprünglichen Code operator<< für Klassenvorlage Property<> ist keine Funktionsvorlage, sondern eine „normale“ Funktion, die bei Bedarf mit der Klassenvorlage instanziiert wird. Es ist das, was wir a nennen Vorlagenentität.

Aber die Definition die Sie in der Quelldatei (.cpp) für den überladenen Operator<< bereitgestellt haben, ist für eine Funktionsvorlage und nicht für eine gewöhnliche Funktion. Also zur Aussage std::cout << num << “\n”; der Linker kann die Definition/Implementierung nicht finden, die der gewöhnlichen Überladung entspricht operator<< wofür du die Freundschaftserklärung hattest.

Es gibt zwei Möglichkeiten, dies zu lösen:

Methode 1

Fügen Sie der Friend-Deklaration eine separate Parameterklausel hinzu.

template <class PropertyType>
class Property {
    PropertyType m_property;
public:
    const PropertyType& operator=(const PropertyType& value);
    template<typename T>  //parameter cluase added here
//---------------------------------------------------vvvvv----------------------->const added here
    friend std::ostream& operator<<(std::ostream& os,const Property<T>& other);
};
template <class PropertyType>
//----------------------------------------vvvvv---------------------------------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
    os << other.m_property;
    return os;
} 

Demo

Methode 2

Hier stellen wir eine Vorwärtsdeklaration für die Überladenen bereit operator<<.

//forward declaration for class template Property
template<typename T> class Property;

//forward declaration for overloaded operator<< 
template<typename T> std::ostream& operator<<(std::ostream&,const Property<T>&);//note the const in the second parameter
template <class PropertyType>
class Property {
    PropertyType m_property;
public:
    const PropertyType& operator=(const PropertyType& value);
//---------------------------------vvvvvvvvvvvvvv---------------------------------> angle brackets used here
    friend std::ostream& operator<<<PropertyType>(std::ostream& os,const Property<PropertyType>& other);//also note the const in the second parameter
}; 
template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
    m_property = value;
    return m_property;
}

template <class PropertyType>
//----------------------------------------vvvvv---------------------------------->const added here 
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
    os << other.m_property;
    return os;
}

Demo

Methode 3

Wenn Sie die Implementierung in der Quelldatei anstelle der Header-Datei bereitstellen möchten, sollten Sie hinzufügen

template std::ostream& operator<<(std::ostream& os, Property<int>& other);

innerhalb der Quelldatei zusätzlich zum Hinzufügen einer Vorlagenparameterklausel für die Friend-Deklaration, wie unten gezeigt:

Klasse.h

#ifndef MYCLASS_H
#define MYCLASS_H
#include <iostream>
template <class PropertyType>
class Property {
    PropertyType m_property;
public:
    const PropertyType& operator=(const PropertyType& value);
    template<typename T>  //parameter clause added
//---------------------------------------------------vvvvv--------------------->const added here
    friend std::ostream& operator<<(std::ostream& os,const Property<T>& other);
}; 


#endif

Klasse.cpp

#include "class.h"

template <class PropertyType>
const PropertyType& Property<PropertyType>::operator=(const PropertyType& value) {
    m_property = value;
    return m_property;
}

template<typename PropertyType>
//----------------------------------------vvvvv------->const added here
std::ostream& operator<<(std::ostream& os,const Property<PropertyType>& other) {
    os << other.m_property;
    return os;
}
template class Property<int>;
template std::ostream& operator<<(std::ostream& os,const Property<int>& other);


main.cpp


#include <iostream>

#include "class.h"
int main() {
    Property<int> num;
    num = 100;
    std::cout << num << "\n";
}

Demo

Zu den von mir vorgenommenen Änderungen gehören:

  1. Für die Friend-Deklaration wurde eine separate Template-Parameter-Klausel hinzugefügt. Dies ist so, dass die Friend-Deklaration für eine Funktionsvorlage gilt. Darüber hinaus geben wir einen anderen Typparameter namens an T und nicht PropertyType because otherwise the new Art der Immobiliewill shadow the outerPropertyType‘.

  2. Low-Level hinzugefügt const zum zweiten Parameter des überladenen opeartor<<.

  3. In method3, innerhalb der Quelldatei (class.cpp), hinzugefügt

template std::ostream& operator<<(std::ostream& os,const Property<int>& other);

für die überladene Nicht-Member-Funktion operator<<.

  • Vielen Dank für Ihre Antwort. aber meine Frage hängt von der Trennung der Deklaration von der Definition (.h, .cpp) ab. Ich weiß, dass alles funktioniert, wenn alles in einer .h-Datei zusammengefasst wird.

    – König der Löwen

    19. April um 16:55 Uhr

  • @LionKing Du sagtest: “Ich weiß, dass alles funktioniert, wenn alles in einer .h-Datei zusammengefasst wird.”. Nein, selbst wenn Sie alles in dieselbe Header-Datei einfügen, wird Ihr gegebenes Snippet nicht funktionieren. Am Anfang Ihrer Frage wird immer noch ein Linker-Fehler erwähnt. Demo. Der Grund, den ich in meiner Antwort erklärt habe, ist, dass die Friend-Deklaration nicht mit der Definition übereinstimmt, da die Friend-Deklaration für eine gewöhnliche Funktion gilt, während die Definition außerhalb der Klassenvorlage für eine Vorlagenfunktion gilt.

    – Anoop Rana

    19. April um 17:38 Uhr


  • @LionKing Wenn Sie die Implementierung in der Quelldatei getrennt halten möchten, sollten Sie auch hinzufügen template std::ostream& operator<<(std::ostream& os, Property<int>& other); in Ihrer Quelldatei. Sehen Methode 3 in meiner Antwort.

    – Anoop Rana

    19. April um 17:52 Uhr

  • “Methode 3” funktioniert gut, aber ich habe einige Fragen zu der von Ihnen vorgenommenen Änderung: (1)- In class .h Warum haben Sie “template” vor die Deklaration von gesetzt operator<< und warum haben Sie einen anderen Vorlagennamen eingegeben T und nicht PropeprtyType? (2)- Warum haben Sie entfernt const aus Parameter zwei der Friend-Funktion operator<<? (3)- Was ist der Vorteil dieser Linie template std::ostream& operator<<(std::ostream& os, Property<int>& other); obwohl wir gesetzt haben template class Property<int>; bisher? (4)- Und schließlich, funktionieren diese Regeln auf allen Compilern?

    – König der Löwen

    19. April um 18:20 Uhr

  • @König der Löwen 1) Die separate Parameterklausel template<typename T> wird benötigt, damit die Friend-Deklaration für eine Funktionsvorlage und nicht für eine normale Funktion ist. Außerdem geben wir den Typparameter als an T und nicht PropertyType weil sonst die neue PropertyType Wille Schatten das Äußere PropertyType. 2) Ich habe die entfernt const aus dem zweiten Parameter versehentlich beim Kopieren und Einfügen von Code aus Ihrem ursprünglichen Beispiel. Ich habe die hinzugefügt const zurück in Methode 3 meiner bearbeiteten Version. Sehen Sie sich meine bearbeitete Methode 3 an.

    – Anoop Rana

    19. April um 18:31 Uhr


1019130cookie-checkWie deklarieren wir eine Friend-Funktion mit einer Klassenvorlage in einer .h-Datei und definieren sie in einer .cpp-Datei (nicht alle in einer Header-Datei)?

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy