Ich habe jetzt einige der Fragen zu meinem Problem auf StackOverflow.com gelesen, und keine davon scheint mein Problem zu lösen. Oder ich habe es vielleicht falsch gemacht … Die überladen <<
funktioniert, wenn ich es in eine Inline-Funktion mache. Aber wie mache ich es in meinem Fall?
warning: friend declaration std::ostream& operator<<(std::ostream&, const D<classT>&)' declares a non-template function
warning: (if this is not what you intended, make sure the function template has already been declared and add <> after the function name here) -Wno-non-template-friend disables this warning
/tmp/cc6VTWdv.o:uppgift4.cc:(.text+0x180): undefined reference to operator<<(std::basic_ostream<char, std::char_traits<char> >&, D<int> const&)'
collect2: ld returned 1 exit status
Der Code:
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const;
classT operator=(const D<classT>& rhs);
friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
classT d;
};
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}
template <class classT>
ostream& operator<<(ostream &os, const D<classT>& rhs)
{
os << rhs.d;
return os;
}
Dies ist eine dieser häufig gestellten Fragen, die unterschiedliche Ansätze haben, die ähnlich, aber nicht wirklich gleich sind. Die drei Ansätze unterscheiden sich darin, wen Sie zum Freund Ihrer Funktion erklären – und dann, wie Sie sie implementieren.
Der Extrovertierte
Deklarieren Sie alle Instanziierungen der Vorlage als Freunde. Dies haben Sie als Antwort akzeptiert und auch die meisten anderen Antworten schlagen vor. Bei diesem Ansatz öffnen Sie unnötigerweise Ihre spezielle Instanziierung D<T>
indem Sie alle zu Freunden erklären operator<<
Instanziierungen. Das ist, std::ostream& operator<<( std::ostream &, const D<int>& )
hat Zugriff auf alle Interna von D<double>
.
template <typename T>
class Test {
template <typename U> // all instantiations of this template are my friends
friend std::ostream& operator<<( std::ostream&, const Test<U>& );
};
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& ) {
// Can access all Test<int>, Test<double>... regardless of what T is
}
Die Introvertierten
Deklarieren Sie nur eine bestimmte Instanz des Einfügeoperators als Freund. D<int>
kann den Einfügungsoperator mögen, wenn er auf sich selbst angewendet wird, aber er will nichts damit zu tun haben std::ostream& operator<<( std::ostream&, const D<double>& )
.
Dies kann auf zwei Arten erfolgen, wobei der einfache Weg so ist, wie @Emery Berger vorgeschlagen hat, der den Operator inliniert – was auch aus anderen Gründen eine gute Idee ist:
template <typename T>
class Test {
friend std::ostream& operator<<( std::ostream& o, const Test& t ) {
// can access the enclosing Test. If T is int, it cannot access Test<double>
}
};
In dieser ersten Version sind Sie nicht Vorlage erstellen operator<<
sondern eine nicht auf Vorlagen basierende Funktion für jede Instanziierung der Test
Vorlage. Auch hier ist der Unterschied subtil, aber dies entspricht im Grunde dem manuellen Hinzufügen: std::ostream& operator<<( std::ostream&, const Test<int>& )
wenn Sie instanziieren Test<int>
und eine weitere ähnliche Überladung beim Instanziieren Test
mit double
oder mit jedem anderen Typ.
Die dritte Version ist umständlicher. Ohne den Code zu inlinen und mit der Verwendung einer Vorlage können Sie eine einzelne Instanziierung der Vorlage zu einem Freund Ihrer Klasse erklären, ohne sich dafür zu öffnen alle andere Instanziierungen:
// Forward declare both templates:
template <typename T> class Test;
template <typename T> std::ostream& operator<<( std::ostream&, const Test<T>& );
// Declare the actual templates:
template <typename T>
class Test {
friend std::ostream& operator<< <T>( std::ostream&, const Test<T>& );
};
// Implement the operator
template <typename T>
std::ostream& operator<<( std::ostream& o, const Test<T>& t ) {
// Can only access Test<T> for the same T as is instantiating, that is:
// if T is int, this template cannot access Test<double>, Test<char> ...
}
Ausnutzen des Extrovertierten
Der subtile Unterschied zwischen dieser dritten Option und der ersten besteht darin, wie sehr Sie sich anderen Klassen öffnen. Ein Beispiel für Missbrauch in der extrovertiert Version wäre jemand, der Zugriff auf Ihre Interna erhalten möchte und dies tut:
namespace hacker {
struct unique {}; // Create a new unique type to avoid breaking ODR
template <>
std::ostream& operator<< <unique>( std::ostream&, const Test<unique>& )
{
// if Test<T> is an extrovert, I can access and modify *any* Test<T>!!!
// if Test<T> is an introvert, then I can only mess up with Test<unique>
// which is just not so much fun...
}
}
Sie können so keinen Freund deklarieren, Sie müssen dafür einen anderen Vorlagentyp angeben.
template <typename SclassT>
friend ostream& operator<< (ostream & os, const D<SclassT>& rhs);
Hinweis SclassT
damit es nicht schattiert classT
. Beim Definieren
template <typename SclassT>
ostream& operator<< (ostream & os, const D<SclassT>& rhs)
{
// body..
}
Dies funktionierte für mich ohne Compiler-Warnungen.
#include <iostream>
using namespace std;
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const {
return (d > rhs.d);
}
classT operator=(const D<classT>& rhs);
friend ostream& operator<< (ostream & os, const D& rhs) {
os << rhs.d;
return os;
}
private:
classT d;
};
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}
Ich denke, man sollte sich erst gar nicht anfreunden.
Sie können einen öffentlichen Methodenaufruf print erstellen, etwa so (für eine Nicht-Vorlagenklasse):
std::ostream& MyClass::print(std::ostream& os) const
{
os << "Private One" << privateOne_ << endl;
os << "Private Two" << privateTwo_ << endl;
os.flush();
return os;
}
und dann außerhalb der Klasse (aber im selben Namensraum)
std::ostream& operator<<(std::ostream& os, const MyClass& myClass)
{
return myClass.print(os);
}
Ich denke, es sollte auch für die Vorlagenklasse funktionieren, aber ich habe es noch nicht getestet.
Bitte schön:
#include <cstdlib>
#include <iostream>
using namespace std;
template <class T>
T my_max(T a, T b)
{
if(a > b)
return a;
else
return b;
}
template <class classT>
class D
{
public:
D(classT in)
: d(in) {};
bool operator>(const D& rhs) const { return d > rhs.d;};
classT operator=(const D<classT>& rhs);
template<class classT> friend ostream& operator<< (ostream & os, const D<classT>& rhs);
private:
classT d;
};
template<class classT> ostream& operator<<(ostream& os, class D<typename classT> const& rhs)
{
os << rhs.d;
return os;
}
int main()
{
int i1 = 1;
int i2 = 2;
D<int> d1(i1);
D<int> d2(i2);
cout << my_max(d1,d2) << endl;
return 0;
}
Dazu gab es kürzlich eine Frage, die aufschlussreich sein könnte: stackoverflow.com/questions/4571611/virtual-operator
– Daniel Trebbien
11. Januar 2011 um 16:52 Uhr
@Daniel – es nimmt nicht das Problem auf, das ich beim Überladen für eine Vorlagenklasse habe
– Sternkorn
11. Januar 2011 um 16:55 Uhr
Ich denke, es ist besser, wenn Sie die Frage nicht mit einer gegebenen Antwort ändern. Es macht es schwieriger zu bestimmen, was das ursprüngliche Problem war. Vielleicht möchten Sie eine hinzufügen BEARBEITEN am Ende mit der Änderung, dass gelöst das Problem, aber ich finde es verwirrend, wenn sich Fragen im Laufe der Zeit ändern und ich den Verlauf aufrufen muss, um zu sehen, was überhaupt gefragt wurde.
– David Rodríguez – Dribeas
11. Januar 2011 um 19:13 Uhr