Frage

Ich versuche, ein Verfahren für einen binären Baum zu implementieren, die einen Stream zurückgibt. Ich mag den Strom in einer Methode zurückgegeben verwenden, um den Baum auf dem Bildschirm zu zeigen oder den Baum in einer Datei zu speichern:

Diese beiden Methoden sind in der Klasse des binären Baumes:

Erklärungen:

void streamIND(ostream&,const BinaryTree<T>*);
friend ostream& operator<<(ostream&,const BinaryTree<T>&);

template <class T>
ostream& operator<<(ostream& os,const BinaryTree<T>& tree) {
    streamIND(os,tree.root);
    return os;
}

template <class T>
void streamIND(ostream& os,Node<T> *nb) {
    if (!nb) return;
    if (nb->getLeft()) streamIND(nb->getLeft());
    os << nb->getValue() << " ";
    if (nb->getRight()) streamIND(nb->getRight());
}

Diese Methode ist in UsingTree Klasse:

void UsingTree::saveToFile(char* file = "table") {
    ofstream f;
    f.open(file,ios::out);
    f << tree;
    f.close();
}

Also habe ich den Betreiber überlastet "<<" der BinaryTree Klasse zu verwenden: cout << Baum und ofstream f << Baum, aber ich die nächste Fehlermeldung angezeigt: undefined reference to `operator << (std :: basic_ostream > &, BinaryTree &) "

P. S. Der Baum speichert Wortobjekte (eine Zeichenfolge mit einem int).

Ich hoffe, dass Sie mein schlechtes Englisch verstehen. Danke! Und ich möchte einen guten Text für Anfänger über STL wissen, welche alle notwendigen erklärt, weil ich all meine Zeit in Fehler wie Abfall.

EDIT: Baum in saveToFile () erklärt wird:. BinaryTree Baum

War es hilfreich?

Lösung

Das Problem ist, dass der Compiler nicht versucht, die auf Vorlagen operator<< Sie zu nutzen, sondern eine nicht-Templat-Version.

Wenn Sie einen Freund in einer Klasse deklarieren Sie die Deklaration dieser Funktion in dem umschließenden Umfang injizieren. Der folgende Code hat den Effekt erklärt (und nicht definieren) eine kostenlose Funktion, die ein non_template_test Argument durch konstanten Bezug nimmt:

class non_template_test
{
   friend void f( non_template_test const & );
};
// declares here:
// void f( non_template_test const & ); 

Das gleiche geschieht mit Template-Klassen, wenn auch in diesem Fall ist es etwas weniger intuitiv. Wenn Sie erklären (und nicht definieren) eine Friend-Funktion innerhalb des Körpers Template-Klasse, deklarieren Sie eine kostenlose Funktion mit, dass genaue Argumente. Beachten Sie, dass Sie eine Funktion deklarieren, nicht eine Template-Funktion:

template<typename T>
class template_test
{
    friend void f( template_test<T> const & t );
};
// for each instantiating type T (int, double...) declares:
// void f( template_test<int> const & );
// void f( template_test<double> const & );

int main() {
    template_test<int> t1;
    template_test<double> t2;
}

Diese freien Funktionen werden deklariert, aber nicht definiert. Der schwierige Teil hier ist, dass diese freien Funktionen keine Vorlage, sondern regelmäßig kostenlose Funktionen erklärt werden. Wenn Sie die Template-Funktion in den Mix erhalten Sie:

template<typename T> class template_test {
   friend void f( template_test<T> const & );
};
// when instantiated with int, implicitly declares:
// void f( template_test<int> const & );

template <typename T>
void f( template_test<T> const & x ) {} // 1

int main() {
   template_test<int> t1;
   f( t1 );
}

Wenn die Compiler die Hauptfunktion treffen instanziiert es die Vorlage template_test mit Typ int und erklärt die freie Funktion void f( template_test<int> const & ), die nicht als Templat wird. Wenn es um den Anruf f( t1 ) findet gibt es zwei f Symbole, die passen: der Nicht-Template f( template_test<int> const & ) erklärt (und nicht definiert), wenn template_test instanziiert wurde und die Templat-Version, die beide deklariert und bei 1 definiert. Die Nicht-Template-Version hat Vorrang und der Compiler paßt es.

Wenn der Linker die Nicht-Template-Version von f zu lösen versucht, kann es nicht das Symbol finden und es nicht so.

Was können wir tun? Es gibt zwei verschiedene Lösungen. Im ersten Fall machen wir den Compiler Nicht-Template-Funktionen für jeden instanziierenden Typen bereitzustellen. Im zweiten Fall erklären wir die Templat-Version als Freund. Sie sind auf subtile Weise anders, aber in den meisten Fällen gleich.

Mit dem Compiler generieren, um die Nicht-Templat Funktionen für uns:

template <typename T>
class test 
{
   friend void f( test<T> const & ) {}
};
// implicitly

Dies hat den Effekt, wie viele Nicht-Templat-freie Funktionen nach Bedarf zu erstellen. Wenn die Compiler der Freund Deklaration in der Vorlage finden test es nicht nur die Erklärung findet, sondern auch die Umsetzung und fügt sowohl den umgebenden Gültigkeitsbereich.

Making the Templat-Version ein Freund

Um die Vorlage ein Freund machen wir es bereits erklärt haben müssen und dem Compiler sagen, dass der Freund, den wir eigentlich eine Vorlage wollen und kein Nicht-Templat-freie Funktion:

template <typename T> class test; // forward declare the template class
template <typename T> void f( test<T> const& ); // forward declare the template
template <typename T>
class test {
   friend void f<>( test<T> const& ); // declare f<T>( test<T> const &) a friend
};
template <typename T> 
void f( test<T> const & ) {}

In diesem Fall vor f als Vorlage zu erklären, müssen wir übermitteln die Vorlage deklarieren. Um die f Vorlage deklarieren müssen wir zuerst uns darauf, die test Vorlage deklarieren. Der Freund Erklärung wird modifiziert, um die spitzen Klammern enthalten, die identifizieren, dass das Element wir einen Freund machen ist eigentlich eine Vorlage und nicht um eine Freisprechfunktion.

Zurück zum Problem

zurück zu Ihrem speziellen Beispiel gehen, ist die einfachste Lösung, die der Compiler die Funktionen für Sie generieren, indem die Deklaration der Freund Funktion inlining:

template <typename T>
class BinaryTree {
   friend std::ostream& operator<<( std::ostream& o, BinaryTree const & t ) {
      t.dump(o);
      return o;
   }
   void dump( std::ostream& o ) const;
};

Mit diesem Code Sie zwingen den Compiler in einen Nicht-Template operator<< für jeden instanziierten Typ zu erzeugen, und das erzeugte Funktions Delegierten auf dem dump Methode der Vorlage.

Andere Tipps

Sie nicht die Vorlage Operator Erklärung benötigen, und Sie haben den Operator „Freund“ für Ihre Klasse zu erklären zu haben, gewährt Zugang zu den anderen Klassen, in diesem Fall std :: cout

friend std::ostream& operator << ( std::ostream& os, BinaryTree & tree )
{
    doStuff( os, tree );
    return os;
}

recomended Lesung: http://www.parashift.com/c++- FAQ-lite / friends.html

Wenn die << Betreiber Überlastung wollen Sie eine konstante Referenz verwenden:

template <class T>
std::ostream& operator << (std::ostream& os, const BinaryTree<T>& tree) 
{
    // output member variables here... (you may need to make
    // this a friend function if you want to access private
    // member variables...

    return os;
}

Stellen Sie sicher, dass die vollständige Vorlage Definitionen (und nicht nur Prototypen) sind in der Include (d .h, .hpp) Datei als Vorlagen und getrennte Sammlung arbeitet nicht zusammen.

Ich weiß nicht, was Linker @Dribeas verwendet, aber dies kann auf jeden Fall den GNU-Linker verursacht einen nicht definierten Referenzfehler zu geben.

Lizenziert unter: CC-BY-SA mit Zuschreibung
Nicht verbunden mit StackOverflow
scroll top