[英]Overloading operator<< for a templated class
我正在尝试为返回流的二叉树实现一个方法。 我想使用方法中返回的流来显示屏幕中的树或将树保存在文件中:
这两个方法都在二叉树的类中:
声明:
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类中:
void UsingTree::saveToFile(char* file = "table") {
ofstream f;
f.open(file,ios::out);
f << tree;
f.close();
}
所以我重载了BinaryTree类的运算符“<<”以使用:cout << tree和ofstream f << tree,但是我收到了下一条错误消息:对`operator <<的未定义引用(std :: basic_ostream>&,二叉树&)”
PS树存储Word对象(带有int的字符串)。
我希望你能理解我的英语不好。 谢谢! 而且我想知道一个关于STL的初学者的好文本,它解释了所有必要的,因为我浪费了所有时间在这样的错误。
编辑:saveToFile()中的树被声明:BinaryTree <Word>树。
问题是编译器没有尝试使用模板化operator<<
you provided,而是使用非模板化版本。
当您在类中声明一个朋友时,您将在封闭范围内注入该函数的声明。 以下代码具有声明(而非定义)自由函数的效果,该函数通过常量引用获取non_template_test
参数:
class non_template_test
{
friend void f( non_template_test const & );
};
// declares here:
// void f( non_template_test const & );
模板类也是如此,即使在这种情况下它也不那么直观。 当您在模板类主体中声明(而不是定义)友元函数时,您将声明具有该确切参数的自由函数。 请注意,您要声明一个函数,而不是模板函数:
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;
}
这些自由函数已声明但未定义。 这里棘手的部分是那些自由函数不是模板,而是声明了常规自由函数。 将模板函数添加到混合中时,您会得到:
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函数时,它实例化了类型为int
的模板template_test
,并声明了未template_test
化的自由函数void f( template_test<int> const & )
。 当找到调用f( t1 )
,有两个匹配的f
符号:实例化template_test时声明(和未定义)的非模板f( template_test<int> const & )
以及声明和定义的template_test
化版本1
。 非模板化版本优先,编译器匹配它。
当链接器尝试解析f
的非模板化版本时,它无法找到符号,因此失败。
我们能做什么? 有两种不同的解决方案。 在第一种情况下,我们使编译器为每个实例化类型提供非模板化函数。 在第二种情况下,我们将模板化版本声明为朋友。 它们略有不同,但在大多数情况下是等价的。
让编译器为我们生成非模板化函数:
template <typename T>
class test
{
friend void f( test<T> const & ) {}
};
// implicitly
这具有根据需要创建尽可能多的非模板化自由函数的效果。 当编译器在模板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 & ) {}
在这种情况下,在将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;
};
使用该代码,您将强制编译器为每个实例化类型生成非模板化operator<<
,并且生成的函数委托模板的dump
方法。
你不需要模板操作符声明,你必须声明你的类的操作符“friend”授予其他类的访问权限,在本例中为std :: cout
friend std::ostream& operator << ( std::ostream& os, BinaryTree & tree ) { doStuff( os, tree ); return os; }
当重载<<
运算符时,你想使用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;
}
确保完整的模板定义(而不仅仅是原型)在include(即.h,.hpp)文件中作为模板,并且单独的编译不能一起工作。
我不知道@Dribeas正在使用什么链接器,但这肯定会导致GNU链接器给出未定义的引用错误。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.