2012-03-26 55 views
2

我想寫一個二叉樹。爲什麼下面的代碼報告錯誤C2039,''< <':不是'btree < T>''的成員,儘管< <運算符已被聲明爲btree類中的友元函數?即使X是Y的朋友,「X不是Y的成員」?

#include<iostream> 
using namespace std; 

template<class T> 
class btree 
{ 
public: 
    friend ostream& operator<<(ostream &,T); 
}; 

template<class T> 
ostream& btree<T>::operator<<(ostream &o,T s) 
{ 
    o<<s.i<<'\t'<<s.n; 
    return o; 
} 
+3

http://sscce.org/(尤其是** **簡單)。 – Griwes 2012-03-26 15:47:08

+0

該代碼看起來很冗長,但問題出現在類btree以及緊隨該類之後的重載<<運算符函數中。 – user1232138 2012-03-26 15:57:47

+0

@OP,每當我看到如此高的滾動條時,就表示錯誤的測試用例。刪除必要的內容,然後它將是「簡單,自包含的正確示例」。 – Griwes 2012-03-26 16:05:50

回答

3

通過聲明操作的朋友,你告訴編譯器查找功能

ostream& operator<<(ostream &,T); 

其中T是完全相同的類型B樹類模板進行實例化。 (例如:btree<Node>,實際簽名會ostream& operator<<(ostream &, Node); - 假設你HACE成員iNoden

此功能將可以訪問類btree<T>的private和protected成員(變量和函數)的所有實例T,但它實際上並不是該類的成員(因爲它不包含friend關鍵字)。

操作定義您提供的是一個操作符是模板類B樹中的一員,因爲如果你有宣佈

template<class T> 
class btree 
{ 
public: 
    ostream& operator<<(ostream &,T); 
}; 

這是由於包含的是btree<T>::前綴(即指定功能/操作員屬於哪個類)。

由於該類中沒有相應的運算符聲明(請參閱上述對朋友聲明的描述),因此編譯器會抱怨。

要解決它,你要麼

  • 保持朋友聲明,刪除操作確定指標btree<T>::前綴和template<class T>並更改第二個參數類型btree<Type>&,其中Type是的一個你期望btree模板被實例化的類型(例如Node) - 然後爲其他類型提供類似的定義
  • 或移除聲明friend關鍵字在類和從兩個聲明定義如現在操作者應該對整個B樹工作(可經由隱式地提供除去T參數*this)。
  • 或者,你可以聲明朋友經營者作爲模板進行實驗,但需要更多的修改:(閱讀更多關於forward declaration

template<class T> btree; // forward declaration of class btree 

// forward declare operator (or move definition here) 
template<class T> 
ostream& operator<<(ostream &o, btree<T>& s); 

// declare operator as template friend 
template<class T>    
class btree    
{    
public:    
    friend ostream& operator<< <> (ostream &, bree<T>&); 
    // note <> after operator name to denote template with no new template parameters 
}; 

注意,上面我認爲你想輸出整棵樹(即在btree對象上調用operator<<)。從代碼中不清楚這是否是你的意圖(class btree沒有成員in)。 如果不是,並且您想要調用運算符的類型是btree的實際模板參數,則不需要從T更改模板運算符的第二個參數,但是也不需要聲明它作爲類btree的friend作爲運算符獨立於btree。如果i和/或n在該類別中是私密的,則您確實需要將其聲明爲類別的朋友,該類別的成員in您正在訪問定義者(例如上面的節點)。關於丟失btree<T>::(或Node::)的概念仍然適用,因爲運營商不屬於任何類別。

夫婦更多的事情,假設你去與朋友聲明:

  • 到操作的類型,第二個參數應該是btree<T>&(強調&),因爲它是更有效地傳遞reference到btree對象比複製整個btree(或使用指針的淺拷貝並使用缺省copy-contructor
  • 第二個參數也應該標記爲const,因爲(可能)您不希望在更改btree對象期間輸出。請注意,在這種情況下,您需要將btree<T>中的某些不變方法標記爲const以允許它進行編譯。 (參見const correctness的FAQ)

EDIT'd幾次說清楚,並確保正確性

1

朋友函數被授予與成員獲得的類的成員相同的訪問權限,但它不是成員。

這就是關鍵字friend的全部要點,以便對非成員進行訪問。

由於你的operator<<不使用btree<T>,沒有理由使它成爲btree<T>的朋友。

所以,我認爲你的意思

friend ostream& operator<<(ostream &, const mydata&); 

class mydata

+0

但在這種情況下有什麼問題? – user1232138 2012-03-26 15:51:03

+1

@ user1232138您正在聲明一個非模板的朋友,而您正在使用錯誤的參數數量定義模板成員。 – 2012-03-26 16:14:34

+0

你幾乎達到了標準...看到我的回答 – 2012-03-27 00:05:12

0

替換:

template<class T> 
ostream& btree<T>::operator<<(ostream &o,T s) 
{ 
    o<<s.i<<'\t'<<s.n; 
    return o; 
} 

有:

ostream& operator<<(ostream &o, const mydata &s) 
{ 
    o<<s.i<<'\t'<<s.n; 
    return o; 
} 

由於本沃伊特提到,錯誤是告訴你,這個函數是btree成員。此外,您似乎僅爲mydata定義了該功能,因爲它期望si成員。

+0

仍然不能正常工作。 – user1232138 2012-03-26 16:03:28

+1

由於該操作員需要訪問私人成員,因此它需要成爲「類mydata」的「好友」。 – 2012-03-26 16:39:29

7

template <typename T> 
class BTree 
{ 
    // ... 
    friend std::ostream& operator<<(std::ostream&, T); 
    // ... 
}; 

你告訴編譯器有一個非模板免費功能

std::ostream& operator<<(std::ostream&, Type) 

你發生任何類型的實例B樹了。但是你永遠不會提供這樣的功能 。您提供的定義適用於成員 ,但作爲成員函數,您的operator<<需要太多參數。

鑑於BTree是一個通用類型,它不應該提供 顯示其包含的元素的手段;這取決於包含的元素 類型。是什麼讓感覺是一樣的東西:

template <typename T> 
class BTree 
{ 
    struct Node 
    { 
     // ... 
     void display(std::ostream& dest, int indent) const; 
    }; 

    // ... 
    void display(std::ostream& dest) const; 
    friend std::ostream& operator<<(std::ostream& dest, BTree const& tree) 
    { 
     tree.display(dest); 
     return dest; 
    } 
}; 

template <typename T> 
void BTree::display(std::ostream& dest) const 
{ 
    if (myRoot == NULL) { 
     dest << "empty"; 
    } else { 
     myRoot->display(dest, 0); 
    } 
} 

template <typename T> 
void BTree::Node::display(std::ostream& dest, int indent) const 
{ 
    dest << std::string(indent, ' ') << data; 
    if (myLeft != NULL) { 
     myLeft->display(dest, indent + 2); 
    } 
    if (myRight != NULL) { 
     myRight->display(dest, indent + 2); 
    } 
} 
+0

儘管我同意你的觀點,但我想知道爲什麼你會選擇3而我選擇2,而當我提出基本相同的建議時。 : - \ – 2012-03-29 23:48:23

+0

@OrgnlDave:還有更多的答案,而不是正確的。你的答案令人困惑,並且似乎是錯誤的(儘管我現在看到它只是很差的詞選擇)。可悲的是,這種混亂意味着這是一個不好的答案。修復它,也許他們會被刪除。 – 2012-03-30 00:19:27

+0

@MooingDuck有任何修復它的建議嗎? – 2012-03-30 01:43:26

0

編輯

因爲它是一個友元函數,C++是有點怪。請參閱,朋友函數實際上並未在類中定義,它們在不同的名稱空間中定義。如果您希望它使用模板化成員函數,則必須在類定義內提供operator<<使用的輸出函數。

我推薦以下方法(stream_out)的原因是爲了演示一種簡單的方法來使它成爲一個成員,並且不會誤導您的代碼的讀者,因爲它不是一個聰明的黑客。

,你可以使用一個聰明的黑客,如已在意見被提出時

94%,但它並沒有回答這個基本的問題:你的「朋友」功能不是你的類的成員,除非它的身體是在聲明中給出的,對此沒有別的話要說。

(什麼是當時的其他佔6%,你問,這是不是正常?拷貝構造函數廢話,CLI等不可告人的顛簸在夜間。)

如果你絕對必須的外部包括它,作爲一個成員函數,你會做類似...

template<class T> 
class btree 
{ 
    private: 
    int i; 
    int n; 
    void stream_out(std::ostream& o); 

    public: 
    friend std::ostream& operator<<(std::ostream& o, btree<T> & me) { 
     me.stream_out(o); 
     return o; 
    } 
}; 

template <class T> 
void btree<T>::stream_out(std::ostream& o) 
{ 
    o << i << '\t' << n; 
} 

編輯清除總結了一下。

+0

仍然是相同的錯誤... – user1232138 2012-03-26 18:50:29

+0

這是在同一個文件?你確定兩者都使用命名空間標準?這就是*完全*我如何使用它,它編譯罰款給我。 MSVC2k10 – 2012-03-26 22:36:09

+0

@ user1232138找到並且(很差)解釋了答案!朋友功能很奇怪。見答案。 – 2012-03-26 23:14:20

相關問題