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?
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:
-
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 outer
PropertyType‘.
-
Low-Level hinzugefügt const
zum zweiten Parameter des überladenen opeartor<<
.
-
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<<
.
friend std::ostream& operator<<(std::ostream& os, Property<PropertyType>& other);
ist keine Vorlage, also die Funktion, die Sie deklariert habenclass.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 Ihrefriend operator <<
als Vorlage. Dann wird es funktionieren.– Gupta
19. April um 17:10 Uhr