为templated类重载operator <<

pet*_*rJk 5 c++ templates operator-overloading

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

这两个方法都在二叉树的类中:

声明:

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());
}
Run Code Online (Sandbox Code Playgroud)

这个方法在UsingTree类中:

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

所以我重载了BinaryTree类的运算符"<<"以使用:cout << tree和ofstream f << tree,但是我收到了下一条错误消息:对`operator <<的未定义引用(std :: basic_ostream>&,二叉树&)"

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

我希望你能理解我的英语不好.谢谢!而且我想知道一个关于STL的初学者的好文本,它解释了所有必要的,因为我浪费了所有时间在这样的错误.

编辑:saveToFile()中的树被声明:BinaryTree <Word>树.

Dav*_*eas 8

问题是编译器不是试图使用operator<<您提供的模板,而是使用非模板化版本.

当您在类中声明一个朋友时,您将在封闭范围内注入该函数的声明.以下代码具有声明(而不是定义)non_template_test通过常量引用获取参数的自由函数的效果:

class non_template_test
{
   friend void f( non_template_test const & );
};
// declares here:
// void f( non_template_test const & ); 
Run Code Online (Sandbox Code Playgroud)

模板类也是如此,即使在这种情况下它也不那么直观.当您在模板类主体中声明(而不是定义)友元函数时,您将声明具有该确切参数的自由函数.请注意,您要声明一个函数,而不是模板函数:

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;
}
Run Code Online (Sandbox Code Playgroud)

这些自由函数已声明但未定义.这里棘手的部分是那些自由函数不是模板,而是声明了常规自由函数.将模板函数添加到混合中时,您会得到:

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 );
}
Run Code Online (Sandbox Code Playgroud)

当编译器命中main函数时,它会template_test使用type 实例化模板,int并声明void f( template_test<int> const & )未模板化的自由函数.当它找到调用时f( t1 ),有两个f匹配的符号:实例化f( template_test<int> const & )时声明(和未定义)的非模板以及template_test声明和定义的模板化版本1.非模板化版本优先,编译器匹配它.

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

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

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

template <typename T>
class test 
{
   friend void f( test<T> const & ) {}
};
// implicitly
Run Code Online (Sandbox Code Playgroud)

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

使模板版本成为朋友

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

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 & ) {}
Run Code Online (Sandbox Code Playgroud)

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

回到问题所在

回到你的特定例子,最简单的解决方案是让编译器通过内联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;
};
Run Code Online (Sandbox Code Playgroud)

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