简体   繁体   English

为templated类重载operator <<

[英]Overloading operator<< for a templated class

I'm trying to implement a method for a binary tree which returns a stream. 我正在尝试为返回流的二叉树实现一个方法。 I want to use the stream returned in a method to show the tree in the screen or to save the tree in a file: 我想使用方法中返回的流来显示屏幕中的树或将树保存在文件中:

These two methods are in the class of the binary tree: 这两个方法都在二叉树的类中:

Declarations: 声明:

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());
}

This method is in UsingTree class: 这个方法在UsingTree类中:

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

So I overloaded the operator "<<" of the BinaryTree class to use: cout << tree and ofstream f << tree, but I receive the next error message: undefined reference to `operator<<(std::basic_ostream >&, BinaryTree&)' 所以我重载了BinaryTree类的运算符“<<”以使用:cout << tree和ofstream f << tree,但是我收到了下一条错误消息:对`operator <<的未定义引用(std :: basic_ostream>&,二叉树&)”

PS The tree stores Word objects (a string with an int). PS树存储Word对象(带有int的字符串)。

I hope you understand my poor English. 我希望你能理解我的英语不好。 Thank you! 谢谢! And I'd like to know a good text for beginners about STL which explains all necessary because i waste all my time in errors like this. 而且我想知道一个关于STL的初学者的好文本,它解释了所有必要的,因为我浪费了所有时间在这样的错误。

EDIT: tree in saveToFile() is declared: BinaryTree< Word > tree. 编辑:saveToFile()中的树被声明:BinaryTree <Word>树。

The problem is that the compiler is not trying to use the templated operator<< you provided, but rather a non-templated version. 问题是编译器没有尝试使用模板化operator<< you provided,而是使用非模板化版本。

When you declare a friend inside a class you are injecting the declaration of that function in the enclosing scope. 当您在类中声明一个朋友时,您将在封闭范围内注入该函数的声明。 The following code has the effect of declaring (and not defining) a free function that takes a non_template_test argument by constant reference: 以下代码具有声明(而非定义)自由函数的效果,该函数通过常量引用获取non_template_test参数:

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

The same happens with template classes, even if in this case it is a little less intuitive. 模板类也是如此,即使在这种情况下它也不那么直观。 When you declare (and not define) a friend function within the template class body, you are declaring a free function with that exact arguments. 当您在模板类主体中声明(而不是定义)友元函数时,您将声明具有该确切参数的自由函数。 Note that you are declaring a function, not a template function: 请注意,您要声明一个函数,而不是模板函数:

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;
}

Those free functions are declared but not defined. 这些自由函数已声明但未定义。 The tricky part here is that those free functions are not a template, but regular free functions being declared. 这里棘手的部分是那些自由函数不是模板,而是声明了常规自由函数。 When you add the template function into the mix you get: 将模板函数添加到混合中时,您会得到:

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 );
}

When the compiler hits the main function it instantiates the template template_test with type int and that declares the free function void f( template_test<int> const & ) that is not templated. 当编译器命中main函数时,它实例化了类型为int的模板template_test ,并声明了未template_test化的自由函数void f( template_test<int> const & ) When it finds the call f( t1 ) there are two f symbols that match: the non-template f( template_test<int> const & ) declared (and not defined) when template_test was instantiated and the templated version that is both declared and defined at 1 . 当找到调用f( t1 ) ,有两个匹配的f符号:实例化template_test时声明(和未定义)的非模板f( template_test<int> const & )以及声明和定义的template_test化版本1 The non-templated version takes precedence and the compiler matches it. 非模板化版本优先,编译器匹配它。

When the linker tries to resolve the non-templated version of f it cannot find the symbol and it thus fails. 当链接器尝试解析f的非模板化版本时,它无法找到符号,因此失败。

What can we do? 我们能做什么? There are two different solutions. 有两种不同的解决方案。 In the first case we make the compiler provide non-templated functions for each instantiating type. 在第一种情况下,我们使编译器为每个实例化类型提供非模板化函数。 In the second case we declare the templated version as a friend. 在第二种情况下,我们将模板化版本声明为朋友。 They are subtly different, but in most cases equivalent. 它们略有不同,但在大多数情况下是等价的。

Having the compiler generate the non-templated functions for us: 让编译器为我们生成非模板化函数:

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

This has the effect of creating as many non-templated free functions as needed. 这具有根据需要创建尽可能多的非模板化自由函数的效果。 When the compiler finds the friend declaration within the template test it not only finds the declaration but also the implementation and adds both to the enclosing scope. 当编译器在模板test找到友元声明时,它不仅会查找声明,还会查找实现,并将两者都添加到封闭范围中。

Making the templated version a friend 使模板版本成为朋友

To make the template a friend we must have it already declared and tell the compiler that the friend we want is actually a template and not a non-templated free function: 要使模板成为朋友,我们必须已经声明它并告诉编译器我们想要的朋友实际上是一个模板而不是非模板化的自由函数:

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 this case, prior to declaring f as a template we must forward declare the template. 在这种情况下,在将f声明为模板之前,我们必须转发声明模板。 To declare the f template we must first forward declare the test template. 要声明f模板,我们必须首先转发声明test模板。 The friend declaration is modified to include the angle brackets that identify that the element we are making a friend is actually a template and not a free function. 友元声明被修改为包括尖括号,用于标识我们正在制作朋友的元素实际上是模板而不是自由函数。

Back to the problem 回到问题所在

Going back to your particular example, the simplest solution is having the compiler generate the functions for you by inlining the declaration of the friend function: 回到你的特定例子,最简单的解决方案是让编译器通过内联friend函数的声明为你生成函数:

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;
};

With that code you are forcing the compiler into generating a non-templated operator<< for each instantiated type, and that generated function delegates on the dump method of the template. 使用该代码,您将强制编译器为每个实例化类型生成非模板化operator<< ,并且生成的函数委托模板的dump方法。

you dont need the template operator declaration and you have to declare the operator "friend" for your class to have granted access to other classes, in this case std::cout 你不需要模板操作符声明,你必须声明你的类的操作符“friend”授予其他类的访问权限,在本例中为std :: cout

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

recomended reading: http://www.parashift.com/c++-faq-lite/friends.html 推荐阅读: http//www.parashift.com/c++-faq-lite/friends.html

When overloading the << operator you want to use a const reference: 当重载<<运算符时,你想使用const引用:

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;
}

Make sure the full template definitions (and not just prototypes) are in the include (ie .h, .hpp) file as templates and separate compilation do not work together. 确保完整的模板定义(而不仅仅是原型)在include(即.h,.hpp)文件中作为模板,并且单独的编译不能一起工作。

I don't know what linker @Dribeas is using, but this can definitely cause the GNU linker to give an undefined reference error. 我不知道@Dribeas正在使用什么链接器,但这肯定会导致GNU链接器给出未定义的引用错误。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM