問題描述
我正在嘗試為返回流的二叉樹實現一種方法.我想使用方法中返回的流在屏幕中顯示樹或將樹保存在文件中:
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
不是模板化的.當它找到調用 f( t1 )
時,有兩個 f
符號匹配:非模板 f( template_test
在 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.
這篇關于重載運算符<<對于模板類的文章就介紹到這了,希望我們推薦的答案對大家有所幫助,也希望大家多多支持html5模板網!