重载运算符<<对于模板类 [英] Overloading operator&lt;&lt; for a templated class

查看:42
本文介绍了重载运算符<<对于模板类的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我正在尝试为返回流的二叉树实现一种方法.我想使用方法中返回的流在屏幕中显示树或将树保存在文件中:

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:

声明:

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

该方法在 UsingTree 类中:

This method is in UsingTree class:

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

所以我重载了运算符<<"要使用的 BinaryTree 类: cout <<树和ofstream f<<树,但我收到下一条错误消息: undefined reference to `operator<<(std::basic_ostream >&, BinaryTree&)'

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&)'

附言该树存储 Word 对象(带有 int 的字符串).

P.S. The tree stores Word objects (a string with an int).

我希望你能理解我糟糕的英语.谢谢!我想知道一篇关于 STL 初学者的好文章,它解释了所有必要的内容,因为我把所有的时间都浪费在这样的错误上.

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.

saveToFile() 中的树声明为:BinaryTree<词 > 树.

tree in saveToFile() is declared: BinaryTree< Word > tree.

推荐答案

问题是编译器没有尝试使用您提供的模板化operator<<,而是一个非模板化的版本.

The problem is that the compiler is not trying to use the templated operator<< you provided, but rather a non-templated version.

当您在类中声明友元时,您就是在封闭作用域中注入了该函数的声明.以下代码的作用是声明(而不是定义)一个自由函数,该函数通过常量引用接受 non_template_test 参数:

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:

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

当编译器遇到 main 函数时,它会实例化模板 template_test 类型为 int 并声明自由函数 void f( template_test const &; ) 不是模板化的.当它找到调用 f( t1 ) 时,有两个 f 符号匹配:非模板 f( template_test const & )template_test 实例化时声明(但未定义),并且模板化版本在 1 处声明和定义.非模板版本优先,编译器匹配.

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. 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. The non-templated version takes precedence and the compiler matches it.

当链接器尝试解析 f 的非模板版本时,它找不到符号,因此失败.

When the linker tries to resolve the non-templated version of f it cannot find the symbol and it thus fails.

我们能做什么?有两种不同的解决方案.在第一种情况下,我们让编译器为每个实例化类型提供非模板化函数.在第二种情况下,我们将模板化版本声明为朋友.它们略有不同,但在大多数情况下是相同的.

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.

让编译器为我们生成非模板化函数:

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

这具有根据需要创建尽可能多的非模板化自由函数的效果.当编译器在模板 test 中找到友元声明时,它不仅会找到声明,还会找到实现并将两者都添加到封闭作用域中.

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.

让模板化版本成为朋友

要使模板成为朋友,我们必须已经声明它并告诉编译器我们想要的朋友实际上是模板而不是非模板化的自由函数:

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 & ) {}

在这种情况下,在将 f 声明为模板之前,我们必须先声明模板.要声明 f 模板,我们必须先向前声明 test 模板.友元声明被修改为包含尖括号,用于标识我们要成为友元的元素实际上是一个模板而不是一个自由函数.

In this case, prior to declaring f as a template we must forward declare the template. To declare the f template we must first forward declare the test template. 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.

回到问题

回到您的特定示例,最简单的解决方案是让编译器通过内联友元函数的声明为您生成函数:

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:

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

使用该代码,您将迫使编译器为每个实例化类型生成一个非模板化的 operator<<,并在 dump 方法上生成函数委托模板.

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.

这篇关于重载运算符<<对于模板类的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆