重载运算符<<对于模板类

发布于 2024-08-12 10:51:39 字数 1210 浏览 7 评论 0 原文

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

这两个方法位于二叉树的类中:

声明:

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 <<树和流 f <<树,但我收到下一条错误消息: undefined reference to `operator<<(std::basic_ostream >&, BinaryTree&)'

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

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

编辑: saveToFile() 中的树被声明: BinaryTree<词>树。

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:

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

P.S. The tree stores Word objects (a string with an 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.

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

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(4

吃不饱 2024-08-19 10:51:39

问题是编译器没有尝试使用您提供的模板化运算符<<,而是使用非模板化版本。

当您在类中声明友元时,您将在封闭范围内注入该函数的声明。以下代码具有声明(而不是定义)一个自由函数的效果,该函数通过常量引用接受 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 并声明自由函数 void f( template_test const & ) 未模板化。当它找到调用 f( t1 ) 时,有两个匹配的 f 符号:非模板 f( template_test; 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 模板。友元声明被修改为包含尖括号,用于标识我们要创建友元的元素实际上是一个模板而不是一个自由函数。

回到问题

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

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

使用该代码,您将强制编译器生成一个每个实例化类型的非模板化运算符<<,并且生成的函数委托模板的dump方法。

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

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

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.

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.

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.

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. 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.

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:

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.

偏爱你一生 2024-08-19 10:51:39

您不需要模板运算符声明,并且必须为您的类声明运算符“friend”才能授予对其他类的访问权限,在这种情况下 std::cout

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

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

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::ostream& operator << ( std::ostream& os, BinaryTree & tree )
{
    doStuff( os, tree );
    return os;
}

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

当梦初醒 2024-08-19 10:51:39

重载 << 运算符时,您希望使用 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;
}

When overloading the << operator you want to use a const reference:

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;
}
浪荡不羁 2024-08-19 10:51:39

确保完整的模板定义(而不仅仅是原型)位于包含文件(即 .h、.hpp)中,因为模板和单独的编译不能一起工作。

我不知道 @Dribeas 正在使用什么链接器,但这肯定会导致 GNU 链接器给出未定义的引用错误。

Make sure the full template definitions (and not just prototypes) are in the include (i.e. .h, .hpp) file as templates and separate compilation do not work together.

I don't know what linker @Dribeas is using, but this can definitely cause the GNU linker to give an undefined reference error.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文